[英]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
可以是任何东西, IEnumerable
, Button
, XmlSerializer
, Dictionary<int, int>
, CultureInfo
甚至是你自己创建的一些类型,仅举几个极端的类型。 显然,您永远不能将CultureInfo
传递给接受double
或int
。
错误消息只是编译器说T
可以是任何类型的方式,而不是必须是double
或int
。
解决这个问题的方法之一很简单,就是不要使用泛型! 在我看来,我认为你不应该在这种情况下使用泛型。
当您不关心(或根本不关心)使用何种类型时,应该使用泛型。 例如, 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.