如何在C#中进行模板专业化
你将如何在C#专业化? 我会提出一个问题。 你有一个模板types,你不知道它是什么。 但是你知道它是否从XYZ派生出来,你想调用.alternativeFunc()。 一个好方法是调用一个特定的函数或类,并且在任何派生types的XYZ上调用.alternativeFunc()时,调用normalCall return .normalFunc()。 这将如何在C#中完成?
在C#中,最接近专业化的是使用更具体的过载; 但是,这是脆弱的,并没有涵盖所有可能的用法。 例如:
void Foo<T>(T value) {Console.WriteLine("General method");} void Foo(Bar value) {Console.WriteLine("Specialized method");}
在这里,如果编译器知道编译时的types,它会select最具体的:
Bar bar = new Bar(); Foo(bar); // uses the specialized method
然而….
void Test<TSomething>(TSomething value) { Foo(value); }
即使对于TSomething=Bar
,也会使用Foo<T>
,因为这是在编译时刻烧录的。
另一种方法是在通用方法中使用typestesting – 但是,这通常是一个糟糕的主意,不推荐使用。
基本上,C#只是不希望你使用专门化,除了多态:
class SomeBase { public virtual void Foo() {...}} class Bar : SomeBase { public override void Foo() {...}}
这里Bar.Foo
将始终parsing为正确的覆盖。
假设你正在谈论模板专门化,因为它可以用C ++模板来完成 – 这样的function在C#中是不可用的。 这是因为C#generics在编译过程中不被处理,更多的是运行时的一个特性。
但是,使用C#3.0扩展方法可以达到类似的效果。 下面是一个示例,显示如何为“MyClass”types添加扩展方法,就像模板专业化一样。 但请注意,您不能使用这个来隐藏方法的默认实现,因为C#编译器总是偏好标准方法来扩展方法:
class MyClass<T> { public int Foo { get { return 10; } } } static class MyClassSpecialization { public static void Bar(this MyClass<int> cls) { return cls.Foo + 20; } }
现在你可以写这个:
var cls = new MyClass<int>(); cls.Bar();
如果你想为没有提供专门化时使用的方法有一个默认情况,比我相信写一个通用的“Bar”扩展方法应该做的诀窍:
public static void Bar<T>(this MyClass<T> cls) { return cls.Foo + 42; }
通过添加中间类和字典, 专业化是可能的 。
为了专门研究T,我们创build了一个通用接口,有一个称为(例如)Apply的方法。 对于实现该接口的特定类,定义该类的特定应用方法。 这个中间阶级被称为特质类。
那个traits类可以在generics方法的调用中被指定为参数,然后(当然)总是采取正确的实现。
traits类可以存储在全局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();
看到这个链接到我最近的博客和后续的广泛的描述和样品。
一些build议的答案是使用运行时types信息:本质上比编译时绑定的方法调用慢。
编译器不像C ++那样强制实施专业化。
我build议在PostSharp中寻找一种通常的编译器完成类似于C ++的效果后注入代码的方法。
我正在寻找一个模式来模拟模板专业化。 有些方法在某些情况下可能起作用。 但是,情况如何
static void Add<T>(T value1, T value2) { //add the 2 numeric values }
可以使用语句来select动作,例如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; } }
现在我们可以写,而不必事先知道types:
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; }
如果专门化不仅要为已实现的types调用,还要为派生types调用,可以使用接口的In
参数。 但是,在这种情况下,方法的返回types不能再是genericstypesT
了。
如果你只是想testing一个types是否来自XYZ,那么你可以使用:
theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));
如果是这样的话,你可以将“theunknownobject”强制转换为XYZ并像这样调用alternativeFunc():
XYZ xyzObject = (XYZ)theunknownobject; xyzObject.alternativeFunc();
希望这可以帮助。