Heredar de una clase base genérica, aplicar una restricción e implementar una interfaz en C #

117

Ésta es una cuestión de sintaxis. Tengo una clase genérica que hereda de una clase base genérica y está aplicando una restricción a uno de los parámetros de tipo. También quiero que la clase derivada implemente una interfaz. Por mi vida, parece que no puedo encontrar la sintaxis correcta.

Esto es lo que tengo:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar { ... }

Lo primero que me vino a la mente fue esto:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar, IFoo { ... }

Pero eso es incorrecto ya que hace que T2 necesite implementar IBar e IFoo, no DerivedFoo para implementar IFoo.

Probé un poco de búsqueda en Google, uso de dos puntos, punto y coma, etc., pero me quedé corto. Estoy seguro de que la respuesta es increíblemente simple.

Dan Rigby
fuente
No pude entender la respuesta de @ Adam cuando miré una vez, pero después de 2 minutos pude obtener lo que es, gracias por la respuesta. La clase derivada tiene más de una implementación puede ser este el punto. De todos modos quiero mostrar su notación a los demás. "clase DerivedClass <Type>: ParentClass donde Type: IType". No debe haber nada entre la última clase implementada y la cláusula where.
nurisezgin

Respuestas:

173

Incluya la firma completa de su clase antes de definir restricciones genéricas.

class DerivedFoo<T1, T2> : ParentFoo<T1, T2>, IFoo where T2 : IBar
{
    ...
}
Adam Robinson
fuente
5
Para otros, he internalizado esto como, una clase solo obtiene una cláusula where, y va al final para cualquiera y todas las restricciones de tipo genérico.
Andy V
@Visser Se permite tener múltiples cláusulas where, clase Test <T1, T2> donde T1: Interface1 donde T2: Interface2
bwing
@Visser sí, lo que dijo bwing, también cada cláusula where puede tener múltiples restricciones ... así que la sintaxis de la publicación original es correcta, solo significa algo diferente que el operador quería. where T2 : IBar, IFoo solo significa que T2tiene que implementar ambas interfaces en lugar de DerivedFoo<T1,T2> implementarIFoo
v01pe
18

Mi recomendación: cuando tenga una pregunta sobre la sintaxis del lenguaje C #, lea la especificación; por eso lo publicamos. Querrá leer la sección 10.1.

Para responder a su pregunta específica, el orden de las cosas en una declaración de clase es:

  • atributos, entre corchetes
  • modificadores ("público", "estático", etc.)
  • "parcial"
  • "clase"
  • el nombre de la clase
  • una lista separada por comas de declaraciones de parámetros de tipo dentro de corchetes angulares
  • dos puntos seguidos de una lista de tipos base separados por comas (clase base e interfaces implementadas, la clase base debe ir primero si hay una)
  • restricciones de parámetro de tipo
  • el cuerpo de la clase, rodeado de tirantes
  • un punto y coma

Todo en esa lista es opcional excepto por "clase", el nombre y el cuerpo, pero todo debe aparecer en ese orden si aparece.

Eric Lippert
fuente
95
Eric, aunque te respeto mucho como profesional y aprecio tus comentarios, no puedo evitar sentirme frustrado por lo que parece una respuesta abrasiva. Me está criticando por elegir hacer una pregunta en un sitio de preguntas y respuestas sobre programación en lugar de localizar, descargar y buscar en un documento Word de 503 páginas altamente técnico oculto en un enlace en MSDN. Eso es bastante duro. Este fue el uso más eficiente de mi tiempo y tiene el beneficio adicional de que puede ayudar a otra persona más adelante. El enlace a la especificación C # Lang para los interesados ​​es: msdn.microsoft.com/en-us/vcsharp/aa336809.aspx
Dan Rigby
17
No se pretendía criticar. Existe un sesgo generalizado en la comunicación de texto puro que hace que las declaraciones simples de hechos suenen bruscas y ásperas; Intento leer con caridad cuando se me presenta una lista de datos útiles y le recomiendo que lo haga también. Estoy de acuerdo con mi recomendación; Si tiene preguntas sobre la sintaxis, la especificación las responde definitivamente y comienza con una útil tabla de contenido para localizar definiciones de sintaxis específicas.
Eric Lippert
3
Dan, encontrar la especificación C # es tan simple como ingresar 'C # Spec' en Google y presionar el botón 'Tengo suerte'. Y si es un desarrollador profesional de C #, ya debería tener la especificación C # en formato PDF en su máquina. Además, tampoco quiero criticarte. No estaba acostumbrado a leer las especificaciones antes, pero comencé a leerlas gracias a Jon, Eric y Pavel, quienes siempre citan las especificaciones de C # para cualquier pregunta. Descubrí que la especificación de C #, aunque a veces puede ser difícil de leer, es una excelente manera de aprender sobre el lenguaje.
SolutionYogi
@Eric Lippert: Muy bien. Gracias por su respuesta. Como sugerencia constructiva, sería útil que Microsoft integrara el contenido de la especificación directamente en MSDN, además de que existiera como una descarga separada. La versión de Visual Studio .Net MSDN tiene una versión integrada de la especificación, pero no versiones posteriores. He pensado en comprar el libro de Anders Hejlberg, pero con .Net 4.0 a la vuelta de la esquina todavía soy reacio. amazon.com/C-Programming-Language-3rd/dp/0321562992 Gracias.
Dan Rigby
2
C ++ requiere que una declaración de clase termine en punto y coma. Muchos desarrolladores de C # tienen experiencia en C ++; a veces sus dedos ponen el punto y coma sin que sus cerebros se involucren. :-) Hay una serie de construcciones en C # que toman un semi opcional donde C ++ lo requiere. Es básicamente una conveniencia sutil. Supongo que también le permite llamar sutilmente cuando una declaración de tipo está terminando en lugar de decir una declaración del cuerpo del método.
Eric Lippert
8
public interface IFoo {}
public interface IBar {}

public class ParentFoo<T,T1> { }
public class DerivedFoo<T, T1> : ParentFoo<T, T1>, IFoo where T1 : IBar { }
Stan R.
fuente
2
public class KeyAndValue<T>
{
    public string Key { get; set; }
    public virtual T Value { get; set; }
}

public class KeyAndValue : KeyAndValue<string>
{
    public override string Value { get; set; }
}

Esta es una extensión de las respuestas existentes. El valor predeterminado es stringsi no proporciona un tipo. No implementé una interfaz, pero eso no debería requerir nada diferente de lo habitual.

user875234
fuente