En C #, lo más cercano a la especialización es usar una sobrecarga más específica; sin embargo, es frágil y no cubre todos los usos posibles. Por ejemplo:
void Foo<T>(T value) {Console.WriteLine("General method");}
void Foo(Bar value) {Console.WriteLine("Specialized method");}
Aquí, si el compilador conoce los tipos en la compilación, elegirá el más específico:
Bar bar = new Bar();
Foo(bar);
Sin embargo....
void Test<TSomething>(TSomething value) {
Foo(value);
}
se utilizará Foo<T>
incluso para TSomething=Bar
, ya que se graba en tiempo de compilación.
Otro enfoque es utilizar la prueba de tipo dentro de un método genérico; sin embargo, esto suele ser una mala idea y no se recomienda.
Básicamente, C # simplemente no quiere que trabajes con especializaciones, excepto por el polimorfismo:
class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}
Aquí Bar.Foo
siempre se resolverá la anulación correcta.
static
métodos que toman un tipo genérico. Es decir, el problema señalado en la respuesta de @MarcGravell parece ser eludido mediante una "plantilla" del método basado en un argumento comoMyClass<T>
/MyClass<int>
, en lugar de una plantilla del método para el tipo de "datos" específico (T
/int
).void CallAppropriateBar<T>() { (new MyClass<T>()).Bar(); }
.Añadiendo una clase intermedia y un diccionario, es posible la especialización .
Para especializarnos en T, creamos una interfaz genérica, que tiene un método llamado (por ejemplo) Aplicar. Para las clases específicas que se implementa la interfaz, la definición del método Apply específico para esa clase. Esta clase intermedia se llama clase de rasgos.
Esa clase de rasgos se puede especificar como un parámetro en la llamada del método genérico, que luego (por supuesto) siempre toma la implementación correcta.
En lugar de especificarlo manualmente, la clase de rasgos también se puede almacenar en un archivo global
IDictionary<System.Type, object>
. Luego se puede buscar y listo, tienes una especialización real allí.Si es conveniente, puede exponerlo en un método de extensión.
class MyClass<T> { public string Foo() { return "MyClass"; } } interface BaseTraits<T> { string Apply(T cls); } class IntTraits : BaseTraits<MyClass<int>> { public string Apply(MyClass<int> cls) { return cls.Foo() + " i"; } } class DoubleTraits : BaseTraits<MyClass<double>> { public string Apply(MyClass<double> cls) { return cls.Foo() + " d"; } } // Somewhere in a (static) class: public static IDictionary<Type, object> register; register = new Dictionary<Type, object>(); register[typeof(MyClass<int>)] = new IntTraits(); register[typeof(MyClass<double>)] = new DoubleTraits(); public static string Bar<T>(this T obj) { BaseTraits<T> traits = register[typeof(T)] as BaseTraits<T>; return traits.Apply(obj); } var cls1 = new MyClass<int>(); var cls2 = new MyClass<double>(); string id = cls1.Bar(); string dd = cls2.Bar();
Vea este enlace a mi blog reciente y los seguimientos para obtener una descripción detallada y ejemplos.
fuente
También estaba buscando un patrón para simular la especialización de plantillas. Hay algunos enfoques que pueden funcionar en algunas circunstancias. Sin embargo, ¿qué pasa con el caso?
static void Add<T>(T value1, T value2) { //add the 2 numeric values }
Sería posible elegir la acción utilizando declaraciones, por ejemplo
if (typeof(T) == typeof(int))
. Pero hay una mejor manera de simular la especialización de plantillas reales con la sobrecarga de una sola llamada de función virtual:public interface IMath<T> { T Add(T value1, T value2); } public class Math<T> : IMath<T> { public static readonly IMath<T> P = Math.P as IMath<T> ?? new Math<T>(); //default implementation T IMath<T>.Add(T value1, T value2) { throw new NotSupportedException(); } } class Math : IMath<int>, IMath<double> { public static Math P = new Math(); //specialized for int int IMath<int>.Add(int value1, int value2) { return value1 + value2; } //specialized for double double IMath<double>.Add(double value1, double value2) { return value1 + value2; } }
Ahora podemos escribir, sin tener que conocer el tipo de antemano:
static T Add<T>(T value1, T value2) { return Math<T>.P.Add(value1, value2); } private static void Main(string[] args) { var result1 = Add(1, 2); var result2 = Add(1.5, 2.5); return; }
Si la especialización no solo debe llamarse para los tipos implementados, sino también para los tipos derivados, se podría usar un
In
parámetro para la interfaz. Sin embargo, en este caso, los tipos de retorno de los métodos ya no pueden ser del tipo genéricoT
.fuente
if (type == typeof(int))
y luego volver al tipo genérico con boxeo / unboxing adicionalreturn (T)(object)result;
(porque el tipo solo se conoce lógicamente, no se conoce estáticamente)Algunas de las respuestas propuestas usan información de tipo de tiempo de ejecución: inherentemente más lento que las llamadas a métodos vinculados en tiempo de compilación.
El compilador no aplica la especialización tan bien como lo hace en C ++.
Recomendaría buscar en PostSharp una forma de inyectar código después de que se realiza el compilador habitual para lograr un efecto similar a C ++.
fuente
Creo que hay una manera de lograrlo con .NET 4+ usando resolución dinámica:
static class Converter<T> { public static string Convert(T data) { return Convert((dynamic)data); } private static string Convert(Int16 data) => $"Int16 {data}"; private static string Convert(UInt16 data) => $"UInt16 {data}"; private static string Convert(Int32 data) => $"Int32 {data}"; private static string Convert(UInt32 data) => $"UInt32 {data}"; } class Program { static void Main(string[] args) { Console.WriteLine(Converter<Int16>.Convert(-1)); Console.WriteLine(Converter<UInt16>.Convert(1)); Console.WriteLine(Converter<Int32>.Convert(-1)); Console.WriteLine(Converter<UInt32>.Convert(1)); } }
Salida:
Int16 -1 UInt16 1 Int32 -1 UInt32 1
Lo que muestra que se requiere una implementación diferente para diferentes tipos.
fuente
Si solo desea probar si un tipo se deriva de XYZ, puede usar:
theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));
Si es así, puede lanzar "theunknownobject" a XYZ e invocar AlternativeFunc () de esta manera:
Espero que esto ayude.
fuente
"c++ template specialization"