Предполагая, что вы говорите о специализации шаблонов, поскольку это можно сделать с помощью шаблонов C ++ - подобная функция на самом деле недоступна в C #. Это связано с тем, что универсальные шаблоны C # не обрабатываются во время компиляции и являются функцией среды выполнения.
Однако вы можете добиться аналогичного эффекта, используя методы расширения C # 3.0. Вот пример, который показывает, как добавить метод расширения только для MyClass<int>
типа, что аналогично специализации шаблона. Однако обратите внимание, что вы не можете использовать это, чтобы скрыть реализацию метода по умолчанию, потому что компилятор C # всегда предпочитает стандартные методы методам расширения:
class MyClass<T> {
public int Foo { get { return 10; } }
}
static class MyClassSpecialization {
public static int Bar(this MyClass<int> cls) {
return cls.Foo + 20;
}
}
Теперь вы можете написать это:
var cls = new MyClass<int>();
cls.Bar();
Если вы хотите иметь вариант по умолчанию для метода, который будет использоваться при отсутствии специализации, я считаю, что написание одного универсального Bar
метода расширения должно помочь:
public static int Bar<T>(this MyClass<T> cls) {
return cls.Foo + 42;
}
static
методов расширения - просто методов, которые принимают общий тип. То есть проблему, указанную в ответе @MarcGravell, можно обойти, «создав шаблон» метода на основе аргумента типаMyClass<T>
/MyClass<int>
, а не шаблонизируя метод для конкретного типа «данных» (T
/int
).void CallAppropriateBar<T>() { (new MyClass<T>()).Bar(); }
.При добавлении промежуточного класса и словаря возможна специализация .
Чтобы специализироваться на T, мы создаем общий интерфейс с методом, называемым (например) Apply. Для конкретных классов этот интерфейс реализован, определяя метод Apply, специфичный для этого класса. Этот промежуточный класс называется классом черт.
Этот класс признаков может быть указан как параметр при вызове универсального метода, который (конечно же) всегда принимает правильную реализацию.
Вместо того, чтобы указывать его вручную, класс свойств также можно сохранить в глобальном
IDictionary<System.Type, object>
. Затем его можно найти и вуаля, у вас есть настоящая специализация.Если удобно, вы можете выставить его в методе расширения.
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();
См. Эту ссылку на мой недавний блог и последующие действия для подробного описания и примеров.
источник
Я тоже искал шаблон для имитации специализации шаблона. Есть несколько подходов, которые могут работать при определенных обстоятельствах. Однако как насчет дела
static void Add<T>(T value1, T value2) { //add the 2 numeric values }
Можно было бы выбрать действие с помощью операторов, например
if (typeof(T) == typeof(int))
. Но есть лучший способ смоделировать реальную специализацию шаблона с накладными расходами на один вызов виртуальной функции: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; } }
Теперь мы можем писать, не зная заранее тип:
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; }
Если специализацию следует вызывать не только для реализованных типов, но и для производных типов, можно использовать
In
параметр для интерфейса. Однако в этом случае возвращаемые типы методов больше не могут быть универсального типаT
.источник
if (type == typeof(int))
а затем вернуть общий тип с дополнительным боксом / распаковкойreturn (T)(object)result;
(потому что тип известен только логически, а не статически)Некоторые из предложенных ответов используют информацию о типе времени выполнения: по своей сути медленнее, чем вызовы методов с привязкой во время компиляции.
Компилятор не требует специализации так же хорошо, как в C ++.
Я бы порекомендовал взглянуть на PostSharp, чтобы найти способ внедрить код после того, как обычный компилятор будет выполнен, чтобы добиться эффекта, аналогичного C ++.
источник
Я думаю, что есть способ добиться этого с помощью .NET 4+, используя динамическое разрешение:
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)); } }
Вывод:
Int16 -1 UInt16 1 Int32 -1 UInt32 1
Это показывает, что для разных типов требуется другая реализация.
источник
Если вы просто хотите проверить, является ли тип производным от XYZ, вы можете использовать:
theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));
Если это так, вы можете преобразовать «theunknownobject» в XYZ и вызвать альтернативуFunc () следующим образом:
Надеюсь это поможет.
источник
"c++ template specialization"