c# - sirven - template en c++ pdf
Cómo hacer especialización de plantillas en C# (7)
¿Cómo harías especialización en C #? Voy a plantear un problema. Tienes un tipo de plantilla, no tienes idea de qué se trata. Pero usted sabe si es derivado de XYZ al que desea llamar .alternativeFunc (). Una buena forma es llamar a una función o clase especializada y hacer que normal Call devuelva .normalFunc () mientras tenga la otra especialización en cualquier tipo derivado de XYZ para llamar a .alternativeFunc (). ¿Cómo se haría esto en C #?
Al agregar una clase intermedia y un diccionario, la especialización es posible .
Para especializarse en T, creamos una interfaz genérica, que tiene un método llamado (por ejemplo) Aplicar. Para las clases específicas que implementa la interfaz, definiendo el método Apply specific 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 IDictionary<System.Type, object>
global IDictionary<System.Type, object>
. Entonces se puede mirar hacia arriba 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 link a mi blog reciente y los seguimientos para una extensa descripción y muestras.
Algunas de las respuestas propuestas usan información del tipo de tiempo de ejecución: inherentemente más lenta que las llamadas a métodos en tiempo de compilación.
El compilador no aplica la especialización tan bien como lo hace en C ++.
Yo recomendaría buscar en PostSharp una forma de inyectar código después de que se haga el compilador habitual para lograr un efecto similar a C ++.
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.
Estaba buscando un patrón para simular la especialización de plantillas, también. 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 sentencias, por ejemplo if (typeof(T) == typeof(int))
. Pero hay una mejor manera de simular la especialización de plantilla real 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 saber el tipo por adelantado:
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 invocarse para los tipos implementados, sino también para los tipos derivados, se podría usar un parámetro In
para la interfaz. Sin embargo, en este caso, los tipos de devolución de los métodos ya no pueden ser del tipo genérico T
Si solo quiere probar si un tipo es derivado de XYZ, puede usar:
theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));
Si es así, puede convertir "theunknownobject" en XYZ e invocar alternativeFunc () de esta manera:
XYZ xyzObject = (XYZ)theunknownobject;
xyzObject.alternativeFunc();
Espero que esto ayude.
asumiendo que está hablando de especialización de plantillas, ya que se puede hacer con plantillas de C ++, una característica como esta no está realmente disponible en C #. Esto se debe a que los genéricos C # no se procesan durante la compilación y son más una característica del tiempo de ejecución.
Sin embargo, puede lograr un efecto similar utilizando los métodos de extensión C # 3.0. Aquí hay un ejemplo que muestra cómo agregar el método de extensión solo para el tipo MyClass<int>
, que es como la especialización de plantilla. Sin embargo, tenga en cuenta que no puede usar esto para ocultar la implementación predeterminada del método, porque el compilador de C # siempre prefiere los métodos estándar a los métodos de extensión:
class MyClass<T> {
public int Foo { get { return 10; } }
}
static class MyClassSpecialization {
public static void Bar(this MyClass<int> cls) {
return cls.Foo + 20;
}
}
Ahora puedes escribir esto:
var cls = new MyClass<int>();
cls.Bar();
Si desea tener un caso predeterminado para el método que se usaría cuando no se proporciona especialización, entonces creo que escribir un método de extensión de Bar
genérico debería ser el truco:
public static void Bar<T>(this MyClass<T> cls) {
return cls.Foo + 42;
}
En C #, lo más cercano a la especialización es usar una sobrecarga más específica; sin embargo, esto 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 compilación, elegirá los más específicos:
Bar bar = new Bar();
Foo(bar); // uses the specialized method
Sin embargo....
void Test<TSomething>(TSomething value) {
Foo(value);
}
usará Foo<T>
incluso para TSomething=Bar
, ya que está TSomething=Bar
en tiempo de compilación.
Otro enfoque es usar pruebas de tipo dentro de un método genérico; sin embargo, esta suele ser una idea pobre y no se recomienda.
Básicamente, C # simplemente no quiere que trabajes con especializaciones, a excepción del polimorfismo:
class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}
Aquí Bar.Foo
siempre se resolverá con la anulación correcta.