您将如何在C#中进行专业化?
我会提出一个问题。您有模板类型,不知道它是什么。但是您确实知道它是否源于XYZ
您要调用.alternativeFunc()
。一个很棒的方法是调用一个专用函数或类并normalCall
返回,.normalFunc()
而对任何派生类型的XYZ
call进行其他专门化处理.alternativeFunc()
。用C#怎么做?
Answers:
在C#中,最接近专业化的地方是使用更具体的重载。但是,这很脆弱,并不能涵盖所有可能的用法。例如:
void Foo<T>(T value) {Console.WriteLine("General method");}
void Foo(Bar value) {Console.WriteLine("Specialized method");}
在这里,如果编译器知道编译时的类型,它将选择最具体的类型:
Bar bar = new Bar();
Foo(bar); // uses the specialized method
然而....
void Test<TSomething>(TSomething value) {
Foo(value);
}
将Foo<T>
用于TSomething=Bar
,因为它在编译时已被烧入。
另一种方法是在通用方法中使用类型测试-但是,这通常是一个糟糕的主意,因此不建议使用。
基本上,C#只是不希望您使用专业化知识,除了多态性:
class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}
这里Bar.Foo
将始终解析为正确的替代。
假设您正在谈论可以使用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>
/的arg来“模板化”方法MyClass<int>
,而不是将方法模板化为特定的“数据”类型(T
/ int
)来解决。
void CallAppropriateBar<T>() { (new MyClass<T>()).Bar(); }
。
通过添加中间类和字典,可以实现专门化。
为了专门研究T,我们创建了一个通用接口,该接口具有一种称为(例如)Apply的方法。对于实现接口的特定类,定义特定于该类的方法Apply。此中间类称为特质类。
可以在通用方法的调用中将该特征类指定为参数,然后(当然)始终采用正确的实现。
无需手动指定,traits类也可以存储在global中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;
(因为该类型仅在逻辑上已知,而不是静态已知)
我认为有一种方法可以使用.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,并像下面这样调用AlternativeFunc():
XYZ xyzObject = (XYZ)theunknownobject;
xyzObject.alternativeFunc();
希望这可以帮助。
"c++ template specialization"