c# generics .net-3.5

c# - ¿Puedo hacer un genérico opcional, por defecto a una determinada clase?



generics .net-3.5 (2)

Mi pregunta está relacionada con ¿Existe un enfoque razonable para los parámetros de tipo "predeterminados" en C # Genéricos? , pero usando una clase genérica interna ese enfoque no funciona.

Dado código como este:

using System; public class FooEventArgs<T> : EventArgs { // ... T properties and a constructor } public class Foo<T> { public delegate void EventHandler<FooEventArgs>(object sender, FooEventArgs<T> e); public event EventHandler<FooEventArgs<T>> Changed }

Y con ser usado así:

public class User { public Foo<int> foo1; public Foo<object> foo2; public User() { foo1 = new Foo<int>(); foo2 = new Foo<object>(); foo1.Changed += foo1_Changed; foo2.Changed += foo2_Changed; } protected void foo1_Changed(object sender, FooEventArgs<int> e) { ... } protected void foo2_Changed(object sender, FooEventArgs<object> e) { ... } }

Bueno, me gustaría si pudiera tener el genérico opcional, ya que habrá muchos casos en los que no sé en qué tipo entrará algo. (Los datos provienen de un sistema externo que tiene sus propios tipos de variables). , que luego se convierten en tipos .NET, pero me encuentro en situaciones donde, por ejemplo, un tipo de datos remoto puede convertirse en uno de un par de tipos .NET, o donde es del tipo "cualquier", por lo que el object sería Ser la única respuesta real para ese caso.

La solución que se me ocurrió de inmediato fue la subclasificación (también fue la sugerencia principal en la pregunta vinculada anteriormente):

public class Foo : Foo<object> { public Foo(...) : base(...) { } } public class FooEventArgs : FooEventArgs<object> { public Foo(...) : base(...) { } }

Entonces quiero usarlo así:

public class User { public Foo foo3; public User() { foo3 = new Foo(); foo3.Changed += foo3_Changed; } protected void foo3_Changed(object sender, FooEventArgs e) { ... } }

El problema es que, naturalmente, no funcionará si foo3_Changed acepta FooEventArgs ; necesita FooEventArgs<object> , ya que eso es lo que el evento Foo.Changed pasará (ya que el valor provendrá de Foo<object> ).

Foo.cs(3,1415926): error CS0123: No overload for ''foo3_Changed'' matches delegate ''FooLibrary.Foo<object>.EventHandler<FooLibrary.FooEventArgs<object>>''

¿Hay algo que pueda hacer al respecto, sin duplicar gran parte de la clase?

FooEventArgs<object> otra cosa: un operador implícito para convertir de FooEventArgs<object> a FooEventArgs .

public static implicit operator FooEventArgs(FooEventArgs<object> e) { return new FooEventArgs(...); }

Lamentablemente, esto no parece funcionar, aunque no tengo muy claro por qué:

EditBuffer.cs(13,37): error CS0553: ''FooLibrary.FooEventArgs.implicit operator FooLibrary.FooEventArgs(FooLibrary.FooEventArgs<object>)'': user-defined conversions to or from a base class are not allowed

Entonces, una vez más, ¿hay algo que pueda hacer al respecto o estoy en lo cierto al pensar que es Tough Luck y tendré que contentarme con FooEventArgs<object> (y luego creo que también puedo usar Foo<object> )?


No creo que haya mucho que puedas hacer al respecto, para ser honesto. Podrías hacer que Foo doblemente genérico:

public class Foo<TData, TArgs> where TArgs : FooEventArgs<TData> { public delegate void EventHandler<TArgs>(object sender, TArgs e); public event EventHandler<TArgs> Changed; }

Entonces podrías escribir:

public class Foo : Foo<object, FooEventArgs>

... pero realmente hace las cosas muy complicadas por muy poco beneficio.

También diría que aunque es un poco más detallado incluir el argumento de tipo, lo deja muy claro, mientras que la herencia puede enturbiar las aguas de varias maneras. Me mantendría al margen de la herencia de clase cuando realmente no estás tratando de modelar la especialización del comportamiento.

La razón por la que su conversión implícita no funciona no tiene nada que ver con los genéricos, por cierto, como indica el mensaje de error, no puede declarar una conversión (implícita o explícita) que sube o baja en la jerarquía de herencia. De la sección 6.4.1 de la especificación de C #:

C # solo permite declarar ciertas conversiones definidas por el usuario. En particular, no es posible redefinir una conversión implícita o explícita ya existente.

(Vea esa sección para más detalles.)

Como nota al margen, me parece más común usar la herencia a la inversa para los genéricos, generalmente con interfaces:

public interface IFoo { // Members which don''t depend on the type parameter } public interface IFoo<T> : IFoo { // Members which all use T }

De esa manera, el código puede recibir solo un IFoo sin preocuparse por el lado genérico de las cosas si no necesitan saber T

Desafortunadamente, eso no te ayuda en tu caso específico.


Otra cosa interesante que acabo de descubrir es que puede crear clases genéricas con el mismo nombre pero con diferentes firmas.

class Foo<T> { } class Foo<T,T> { }

Entonces puedes llamar a cualquiera de ellos como sigue:

new Foo<string>(); new Foo<int,double>(); new Foo<string,int>();

Simplemente pensé que era interesante que, a pesar de que ambas clases tienen el mismo nombre, pueden coexistir porque tienen firmas diferentes.

Supongo que así es como funciona la clase Tuple.