繁体   English   中英

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

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

我在C ++中使用过模板,但过去并没有真正尝试使用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)
    {
    }
}

我得到以下编译错误(在GenericBase<T>上的DoStuff()方法中):

错误CS1503参数1:无法从'T'转换为'double'

为什么编译器无法解析调用哪个DoesStuffWithPrimatives.DoStuff(…)重载!?

对于泛型,编译器可以做的最好的事情是假设类型参数(在本例中为T )可以是任何类型,其基数与您指定的相同。 由于您没有指定基数,因此编译器将T视为从Object继承的任何内容,这实际上就是任何东西。

编译器不能决定你的T是双,因为这不会对每种类型的意义T 不是一个double 例如,采用以下通用方法:

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

T为double时,这样可以正常工作,因为你可以用double代替T

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

然而, T可能是其他类似的东西,比如List 在这种情况下,此代码将无法编译:

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

编译器不能假设T是双精度的,因为这样做会破坏T不是双精度的所有地方。

当然,您可以将其强制转换,也可以对对象执行类型检查。

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

代码不起作用的原因是T不限于固定的类型集。 T可以是任何类型:struct,class,delegate,interface ...

当你这样做:

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

它当然不会编译,因为_testValue可以是任何东西, IEnumerableButtonXmlSerializerDictionary<int, int>CultureInfo甚至是你自己创建的一些类型,仅举几个极端的类型。 显然,您永远不能将CultureInfo传递给接受doubleint

错误消息只是编译器说T可以是任何类型的方式,而不是必须是doubleint

解决这个问题的方法之一很简单,就是不要使用泛型! 在我看来,我认为你不应该在这种情况下使用泛型。

当您不关心(或根本不关心)使用何种类型时,应该使用泛型。 例如, List<T>并不关心它存储的类型。 它适用于任何类型。 其他一些类/接口/方法使用具有特定属性的类型:实现某些接口,具有默认构造函数,是类等。这就是为什么我们有泛型类型约束。 但是,始终存在满足这些约束的无限类型。 您始终可以创建实现某些接口的类型或具有默认构造函数的类型。 不存在将泛型类型约束为有限数量类型的泛型约束,就我所关注的“原语”而言。

如上所述,编译器无法确定T是任何重载的有效类型。 但是,可以使用反射来获得正确的方法。 如果没有正确处理,这可能会导致运行时错误并且会产生一些开销。 如果可以有一个静态变量,那么开销可以在很大程度上被否定。 在下面的示例中, 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);
    }
}

如果DoesStuffWithPrimatives类不能是静态的(如果它可以包含几个不同的变量),静态变量应该是方法info, GenericBase的实例构造函数应该使用DoesStuffWithPrimatives变量来处理CreateDelegate

暂无
暂无

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

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