如何在 C# 中进行模板特化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/600978/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 09:50:49  来源:igfitidea点击:

How to do template specialization in C#

c#partial-specialization

提问by

How would you do specialization in C#?

您将如何进行 C# 专业化?

I'll pose a problem. You have a template type, you have no idea what it is. But you do know if it's derived from XYZyou want to call .alternativeFunc(). A great way is to call a specialized function or class and have normalCallreturn .normalFunc()while have the other specialization on any derived type of XYZto call .alternativeFunc(). How would this be done in C#?

我会提出问题。你有一个模板类型,你不知道它是什么。但是您确实知道它是否源自XYZ您想要调用.alternativeFunc(). 一个很好的方法是调用一个专门的函数或类,并有normalCall返回,.normalFunc()同时对XYZ要调用的任何派生类型进行其他专门化.alternativeFunc()。这将如何在 C# 中完成?

回答by Tomas Petricek

Assuming you're talking about template specialization as it can be done with C++ templates - a feature like this isn't really available in C#. This is because C# generics aren't processed during the compilation and are more a feature of the runtime.

假设您正在谈论模板特化,因为它可以用 C++ 模板来完成 - 像这样的功能在 C# 中并不真正可用。这是因为 C# 泛型在编译期间没有被处理,它更多地是运行时的一个特性。

However, you can achieve similar effect using C# 3.0 extension methods. Here is an example that shows how to add extension method only for MyClass<int>type, which is just like template specialization. Note however, that you can't use this to hide default implementation of the method, because C# compiler always prefers standard methods to extension methods:

但是,您可以使用 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;
  }
}

Now you can write this:

现在你可以这样写:

var cls = new MyClass<int>();
cls.Bar();

If you want to have a default case for the method that would be used when no specialization is provided, than I believe writing one generic Barextension method should do the trick:

如果您想为未提供专业化时使用的方法设置默认情况,那么我相信编写一个通用Bar扩展方法应该可以解决问题:

  public static int Bar<T>(this MyClass<T> cls) {
    return cls.Foo + 42;
  }

回答by Jeroen Landheer

If you just want to test if a type is derrived from XYZ, then you can use:

如果您只想测试一个类型是否从 XYZ 派生,那么您可以使用:

theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));

If so, you can cast "theunknownobject" to XYZ and invoke alternativeFunc() like this:

如果是这样,您可以将“theunknownobject”转换为 XYZ 并像这样调用替代函数():

XYZ xyzObject = (XYZ)theunknownobject; 
xyzObject.alternativeFunc();

Hope this helps.

希望这可以帮助。

回答by Marc Gravell

In C#, the closest to specialization is to use a more-specific overload; however, this is brittle, and doesn't cover every possible usage. For example:

在 C# 中,最接近专业化的是使用更具体的重载;然而,这很脆弱,并没有涵盖所有可能的用法。例如:

void Foo<T>(T value) {Console.WriteLine("General method");}
void Foo(Bar value) {Console.WriteLine("Specialized method");}

Here, if the compiler knows the types at compile, it will pick the most specific:

在这里,如果编译器在编译时知道类型,它将选择最具体的:

Bar bar = new Bar();
Foo(bar); // uses the specialized method

However....

然而....

void Test<TSomething>(TSomething value) {
    Foo(value);
}

will use Foo<T>even for TSomething=Bar, as this is burned in at compile-time.

将使用Foo<T>even for TSomething=Bar,因为它在编译时被烧毁。

One other approach is to use type-testing withina generic method - however, this is usually a poor idea, and isn't recommended.

另一种方法是泛型方法中使用类型测试- 然而,这通常是一个糟糕的主意,不推荐。

Basically, C# just doesn't want you to work with specializations, except for polymorphism:

基本上,C# 只是不希望你使用特化,除了多态:

class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}

Here Bar.Foowill always resolve to the correct override.

这里Bar.Foo将始终解析为正确的覆盖。

回答by Barend Gehrels

By adding an intermediate class and a dictionary, specialization is possible.

通过添加一个中间类和一个字典,专业化是可能的

To specialize on T, we create an generic interface, having a method called (e.g.) Apply. For the specific classes that interface is implemented, defining the method Apply specific for that class. This intermediate class is called the traits class.

为了专门研究 T,我们创建了一个通用接口,有一个名为(例如)Apply 的方法。对于实现接口的特定类,定义特定于该类的方法 Apply。这个中间类被称为traits 类。

That traits class can be specified as a parameter in the call of the generic method, which then (of course) always takes the right implementation.

该特性类可以在泛型方法的调用中指定为参数,然后(当然)总是采用正确的实现。

Instead of specifying it manually, the traits class can also be stored in a global IDictionary<System.Type, object>. It can then be looked up and voila, you have real specialization there.

特征类也可以存储在全局IDictionary<System.Type, object>. 然后可以查找它,瞧,您在那里拥有真正的专业化。

If convenient you can expose it in an extension method.

如果方便,您可以在扩展方法中公开它。

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();

See this linkto my recent blog and the follow ups for an extensive description and samples.

请参阅此链接到我最近的博客和后续内容,以获得广泛的描述和示例。

回答by GregC

Some of the proposed answers are using runtime type info: inherently slower than compile-time bound method calls.

一些建议的答案使用运行时类型信息:本质上比编译时绑定方法调用慢。

Compiler does not enforce specialization as well as it does in C++.

编译器不像在 C++ 中那样强制特殊化。

I would recommend looking at PostSharp for a way to inject code after the usual compiler is done to achieve an effect similar to C++.

我建议在完成通常的编译器以实现类似于 C++ 的效果后查看 PostSharp 以获取注入代码的方法。

回答by LionAM

I was searching for a pattern to simulate template specialization, too. There are some approaches which may work in some circumstances. However what about the case

我也在寻找一种模式来模拟模板专业化。有一些方法可能在某些情况下有效。然而案子呢

static void Add<T>(T value1, T value2)
{
    //add the 2 numeric values
}

It would be possible to choose the action using statements e.g. if (typeof(T) == typeof(int)). But there is a better way to simulate real template specialization with the overhead of a single virtual function call:

可以使用语句选择动作,例如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;
    }
}

Now we can write, without having to know the type in advance:

现在我们可以编写,而无需事先知道类型:

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;
}

If the specialization should not only be called for the implemented types, but also derived types, one could use an Inparameter for the interface. However, in this case the return types of the methods cannot be of the generic type Tany more.

如果不仅应为实现类型调用特化,还应为派生类型调用特化,则可In以为接口使用参数。但是,在这种情况下,方法的返回类型不能再是泛型类型T

回答by Drolevar

I think there is a way to achieve it with .NET 4+ using dynamic resolution:

我认为有一种方法可以使用动态分辨率通过 .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));
    }
}

Output:

输出:

Int16 -1
UInt16 1
Int32 -1
UInt32 1

Which shows that a different implementation is called for different types.

这表明为不同的类型调用了不同的实现。