Zakładając, że mówisz o specjalizacji szablonów, ponieważ można to zrobić za pomocą szablonów C ++ - taka funkcja nie jest tak naprawdę dostępna w C #. Dzieje się tak, ponieważ C # typy ogólne nie są przetwarzane podczas kompilacji i są bardziej funkcją środowiska uruchomieniowego.
Można jednak osiągnąć podobny efekt przy użyciu metod rozszerzających C # 3.0. Oto przykład, który pokazuje, jak dodać metodę rozszerzenia tylko dla MyClass<int>
typu, co jest podobne do specjalizacji szablonu. Należy jednak pamiętać, że nie można tego użyć do ukrycia domyślnej implementacji metody, ponieważ kompilator C # zawsze preferuje metody standardowe od metod rozszerzających:
class MyClass<T> {
public int Foo { get { return 10; } }
}
static class MyClassSpecialization {
public static int Bar(this MyClass<int> cls) {
return cls.Foo + 20;
}
}
Teraz możesz napisać to:
var cls = new MyClass<int>();
cls.Bar();
Jeśli chcesz mieć domyślny przypadek dla metody, która byłaby używana, gdy nie podano specjalizacji, to uważam, że napisanie jednej ogólnej Bar
metody rozszerzenia powinno załatwić sprawę:
public static int Bar<T>(this MyClass<T> cls) {
return cls.Foo + 42;
}
static
metod rozszerzających - tylko metody, które przyjmują typ ogólny. Oznacza to, że problem wskazany w odpowiedzi @MarcGravell wydaje się być ominięty przez „tworzenie szablonów” metody opartej na argumencie typuMyClass<T>
/MyClass<int>
, zamiast tworzenia szablonów metody do określonego typu „danych” (T
/int
).void CallAppropriateBar<T>() { (new MyClass<T>()).Bar(); }
.Dodanie klasy pośredniej i słownika umożliwia specjalizację .
Aby specjalizować się w T, tworzymy ogólny interfejs, z metodą o nazwie (np.) Apply. Dla określonych klas ten interfejs jest zaimplementowany, definiując metodę Zastosuj specyficzną dla tej klasy. Ta klasa pośrednia nazywana jest klasą cech.
Ta klasa cech może być określona jako parametr w wywołaniu metody generycznej, która wtedy (oczywiście) zawsze przyjmuje właściwą implementację.
Zamiast określać ją ręcznie, klasa cech może być również przechowywana w pliku globalnym
IDictionary<System.Type, object>
. Następnie można go sprawdzić i voila, masz tam prawdziwą specjalizację.Jeśli jest to wygodne, możesz je wyeksponować metodą rozszerzania.
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();
Zobacz ten link do mojego ostatniego bloga i dalsze informacje, aby uzyskać obszerny opis i próbki.
źródło
Szukałem też wzoru do symulacji specjalizacji szablonów. Istnieje kilka podejść, które mogą działać w pewnych okolicznościach. Jednak co ze sprawą
static void Add<T>(T value1, T value2) { //add the 2 numeric values }
Możliwe byłoby wybranie akcji za pomocą instrukcji np
if (typeof(T) == typeof(int))
. Ale jest lepszy sposób na symulację prawdziwej specjalizacji szablonów z narzutem pojedynczego wywołania funkcji wirtualnej: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; } }
Teraz możemy pisać bez konieczności wcześniejszej znajomości typu:
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; }
Gdyby specjalizacja była wywoływana nie tylko dla implementowanych typów, ale także typów pochodnych, można by użyć
In
parametru dla interfejsu. Jednak w tym przypadku zwracane typy metod nie mogą być już typu ogólnegoT
.źródło
if (type == typeof(int))
Zaczynało wyglądać, że będę musiał zrobić coś okropnego, a następnie wrzucić z powrotem do ogólnego typu z dodatkowym pudełkiem / rozpakowaniemreturn (T)(object)result;
(ponieważ typ jest znany tylko logicznie, a nie statycznie)Niektóre z proponowanych odpowiedzi używają informacji o typie środowiska uruchomieniowego: z natury wolniej niż wywołania metod związane z czasem kompilacji.
Kompilator nie wymusza specjalizacji tak dobrze, jak robi to w C ++.
Poleciłbym przyjrzeć się PostSharp, aby znaleźć sposób na wstrzyknięcie kodu po zakończeniu zwykłego kompilatora, aby uzyskać efekt podobny do C ++.
źródło
Myślę, że jest sposób, aby to osiągnąć dzięki .NET 4+ przy użyciu dynamicznej rozdzielczości:
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)); } }
Wynik:
Int16 -1 UInt16 1 Int32 -1 UInt32 1
Co pokazuje, że dla różnych typów wymagana jest inna implementacja.
źródło
Jeśli chcesz tylko sprawdzić, czy typ pochodzi od XYZ, możesz użyć:
theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));
Jeśli tak, możesz rzucić „theunknownobject” na XYZ i wywołać alternatywnąFunc () w następujący sposób:
Mam nadzieję że to pomoże.
źródło
"c++ template specialization"