简体   繁体   English

C#泛型类调用方法使用基元重载

[英]C# generic class calling method overloads with primitives

I've used templates in C++ but haven't really tried to get too fancy with C# generics in the past. 我在C ++中使用过模板,但过去并没有真正尝试使用C#泛型。 Here's a simplified cut-down version of what I'm trying to do (this would be possible in C++): 这是我正在尝试做的简化的简化版本(这在C ++中是可能的):

class DoesStuffWithPrimatives
{
    public void DoStuff(double value) { }
    public void DoStuff(string value) { }
    public void DoStuff(int value) { }
    public void DoStuff(uint value) { }
    // etc...
}

class GenericBase<T>
{
    private readonly T _testValue;
    private DoesStuffWithPrimatives _doesStuff = new DoesStuffWithPrimatives();
    public GenericBase(T testValue)
    {
        _testValue = testValue;
    }

    public void DoStuff()
    {
        _doesStuff.DoStuff(_testValue);
    }
}

class DoubleContrete : GenericBase<double>
{
    public DoubleContrete() : base(1.54545487)
    {
    }
}

class IntConrete : GenericBase<int>
{
    public IntConrete() : base(80085)
    {
    }
}

I get the following compile error (in the DoStuff() method on GenericBase<T> ): 我得到以下编译错误(在GenericBase<T>上的DoStuff()方法中):

Error CS1503 Argument 1: cannot convert from 'T' to 'double' 错误CS1503参数1:无法从'T'转换为'double'

Why can't the compiler resolve which of the DoesStuffWithPrimatives.DoStuff(…) overloads to call!? 为什么编译器无法解析调用哪个DoesStuffWithPrimatives.DoStuff(…)重载!?

With generics, the best that the compiler can do is assume that the type parameter ( T in this case) can be any type with a base of what you've specified. 对于泛型,编译器可以做的最好的事情是假设类型参数(在本例中为T )可以是任何类型,其基数与您指定的相同。 Since you didn't specify a base, The compiler treats T as anything that inherits from Object , which is literally anything. 由于您没有指定基数,因此编译器将T视为从Object继承的任何内容,这实际上就是任何东西。

The compiler can't decide that your T is a double because that wouldn't make sense for every type of T that isn't a double . 编译器不能决定你的T是双,因为这不会对每种类型的意义T 不是一个double Take, for example, the following generic method: 例如,采用以下通用方法:

public void DoStuff<T>(T param)
{
    DoStuffWithDouble(param);
}

When T is double, then this would work fine, as you can just substitute double for T : T为double时,这样可以正常工作,因为你可以用double代替T

public void DoStuff(double param)
{
    DoStuffWithDouble(param); // param is a double, so no problem
}

However, T could be something else like, say, a List . 然而, T可能是其他类似的东西,比如List In this case, this code wouldn't compile: 在这种情况下,此代码将无法编译:

public void DoStuff(List param)
{
    DoStuffWithDouble(param); // param is not double, this wouldn't compile
}

The compiler cannot make the assumption that T is double because doing so would break everywhere that T is not double. 编译器不能假设T是双精度的,因为这样做会破坏T不是双精度的所有地方。

You can cast it, of course, as well as perform type checks on the object. 当然,您可以将其强制转换,也可以对对象执行类型检查。

public void DoStuff<T>(T param)
{
    if (param is double)
        // Only runs if T is confirmed to be a double, so no chance for errors
        DoStuffWithDouble((double)param); 
}

The reason why the code does not work is that T is not limited to a fixed set of types. 代码不起作用的原因是T不限于固定的类型集。 T can be any type: struct, class, delegate, interface... T可以是任何类型:struct,class,delegate,interface ...

When you do this: 当你这样做:

public void DoStuff()
{
    _doesStuff.DoStuff(_testValue);
}

It of course will not compile because _testValue can be anything, IEnumerable , Button , XmlSerializer , Dictionary<int, int> , CultureInfo or even some types you created yourself, just to name a few of the extreme ones. 它当然不会编译,因为_testValue可以是任何东西, IEnumerableButtonXmlSerializerDictionary<int, int>CultureInfo甚至是你自己创建的一些类型,仅举几个极端的类型。 Obviously, you can never pass a CultureInfo to a method that accepts a double or an int . 显然,您永远不能将CultureInfo传递给接受doubleint

The error message is just the compiler's way of saying T can be any type, not neccessarily double or int . 错误消息只是编译器说T可以是任何类型的方式,而不是必须是doubleint

One of the solutions to this problem is simple, just don't use generics at all! 解决这个问题的方法之一很简单,就是不要使用泛型! In my opinion, I don't think you should use generics in this situtation. 在我看来,我认为你不应该在这种情况下使用泛型。

Generics should be used when you care little about (or don't care at all) what type is used. 当您不关心(或根本不关心)使用何种类型时,应该使用泛型。 For example, List<T> does not care about what type it is storing. 例如, List<T>并不关心它存储的类型。 It works with any type. 它适用于任何类型。 Some other classes/interfaces/methods work with types that has specific attributes: implement certain interfaces, has a default constructor, is a class etc. That's why we have generic type constraints. 其他一些类/接口/方法使用具有特定属性的类型:实现某些接口,具有默认构造函数,是类等。这就是为什么我们有泛型类型约束。 However, there are always a infinite set of types that fulfills these constraints. 但是,始终存在满足这些约束的无限类型。 You can always create a type that implements certain interfaces or has a default constructor. 您始终可以创建实现某些接口的类型或具有默认构造函数的类型。 There does not exist generic constraints that constraint the generic type to a finite number of types, like the "primitives" as far as I'm concerned. 不存在将泛型类型约束为有限数量类型的泛型约束,就我所关注的“原语”而言。

As stated, the compiler can't know for certain that T is a valid type for any of the overloads. 如上所述,编译器无法确定T是任何重载的有效类型。 However it is possible to use reflection to get the proper method. 但是,可以使用反射来获得正确的方法。 Without proper handling this could cause runtime errors of course and has some overhead. 如果没有正确处理,这可能会导致运行时错误并且会产生一些开销。 The overhead can be largely negated if it's possible to have a static variable. 如果可以有一个静态变量,那么开销可以在很大程度上被否定。 In the example below the Action<T> 'dostuffer' will be determined once for each type when it is used: 在下面的示例中, Action<T> 'dostuffer'将在使用时为每种类型确定一次:

class GenericBase<T>
{
    private readonly T _testValue;

    public GenericBase(T testValue)
    {
        _testValue = testValue;
    }


    static readonly DoesStuffWithPrimatives _doesStuff = new DoesStuffWithPrimatives();
    static readonly Action<T> dostuffer = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>),_doesStuff,
        typeof(DoesStuffWithPrimatives).GetMethod(nameof(DoesStuffWithPrimatives.DoStuff), new[]{typeof(T)}));


    public void DoStuff()
    {
        dostuffer(_testValue);
    }
}

If the DoesStuffWithPrimatives class can't be static (if it can consist of several different variables), the static variable should be the method info and the instance constructor of GenericBase should handle the CreateDelegate, using the DoesStuffWithPrimatives variable 如果DoesStuffWithPrimatives类不能是静态的(如果它可以包含几个不同的变量),静态变量应该是方法info, GenericBase的实例构造函数应该使用DoesStuffWithPrimatives变量来处理CreateDelegate

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM