实现泛型的特定案例,而不是从中派生
本文关键字:派生 泛型 案例 实现 | 更新日期: 2023-09-27 17:55:38
我正在尝试写出具有附加功能的泛型类的特定情况,但我不确定为什么会发生以下情况。
假设我有一个泛型类:
class Generic<T>
{
protected T value;
}
如果我写出一个特定的实现,我实际上不能使用我将其缩小到的特定类型:
编辑:我搞砸了,这行不通。
class Generic<float>
{
// This doesn't work
public void Add()
{
value + 1.0f;
}
}
但是如果我从特定版本继承,它确实有效:
class Specific : Generic<float>
{
// This does work
public void Add()
{
value + 1.0f;
}
}
如果有人还在阅读这篇文章,我想指出这似乎是可以通过扩展方法实现的:
class Generic<T>
{
public T value;
}
static class Extension
{
public static void Add (this Generic<float> generic)
{
generic.value += 1.0f;
}
}
缺点似乎是"价值"必须是公共的或内部的。
您的第一次尝试根本不是有效的声明 - 指定类的部分不能指定任何类型参数。
如果您考虑一下,CLR 如何知道是否有可用的专用类型?如果在两个加载的程序集中存在同一泛型类型的两个不同专用化,它会做什么?它必须在首次使用特定类型参数组合时检查所有引用的程序集。(这在编译时无法完成,因为其他类可能只是引用Generic<T>
。
在许多情况下,可以通过约束以对类型有意义的方式使用类型的值。例如,如果使用where T : IComparable<T>
约束T
,则可以使用 Compare
比较任意两个T
值。不幸的是,没有办法以这种方式表示算术运算符,尽管你可能想看看Marc Gravell在MiscUtil中的泛型运算符工作。
虽然我能感受到你的痛苦,但 .NET 泛型中根本没有这样的东西 - 你会想出你真正想要解决的任何问题的替代设计。
在这种情况下:
class Generic<float>
{
// This doesn't work
public void Add()
{
value + 1.0f;
}
}
这并不意味着您正在使用使用 float 作为其泛型类型的泛型类,但这意味着泛型类型的名称在源中是"float"(而不是使用 T,而是使用"float"作为其名称)因此没有转换为浮点型。换句话说,您将通用表示法用作模板,以后可以用实际类型替换(但不是在模板本身中,这就是为什么在C++它们被称为模板)
在此代码中:
class Specific : Generic<float>
{
// This does work
public void Add()
{
value + 1.0f;
}
}
您告诉编译器您希望特定类是泛型类的子类,而它的模板类型将被 float 类型替换。
型类型用于算术可重用性。也就是说,您必须在代码中所有可能的 T 之间编写一些共同的东西。
class Generic<T>
{
protected T value; //it's valid to declare a member whose type is T
public void Add()
{
value + 1.0f; //invalid, because not all T are allowed to add
//with 0.1f by default
//consider T is the type Person
}
public void Print()
{
Type t = typeof(T); //valid, for all T we can get its type
}
}
当你指定了一些T(例如在你的问题中浮点)时,编译器知道T是浮点数,所以给它添加0.1f是有效的。
我也在寻找类似的解决方案,我想你也从 ADA 或这样的编程语言中得到了这种想法。但是,正如其他人所写,创建特定于类型的类定义不是泛型编程,而是专用化,因此最简单的(并且仅在 C# 中)的方法是基于泛型结构创建一个指定的类:
class Generic<T>
{
protected T value;
public Generic(T val)
{
value = val;
}
}
class Generic_float : Generic<float>
{
public Generic_float(float val)
: base(val)
{
}
public void Add()
{
value = value + 1.0f;
}
}
如您所见,我们为指定的情况创建了一个类,具有使用字段和方法扩展结构的额外能力。这个优势对于优化对象的行为非常好,并且能够将类型化的泛型类隐式强制转换为我们的自定义类(请注意,无法强制转换):
public void Test()
{
Generic<float> var1 = new Generic<float>(1.5f);
Generic_float var2 = new Generic_float(2.5f);
var1 = var2; // Works, var links to var2's memory field casted as Generic<float>
var2 = var1; // cannot implicitly convert error, if want to use then have to make explicit conversion
}
不知道您期望的方式是否应该在 C# 或其他托管语言中工作,但也许此解决方法可以为您提供真正想要获得的内容。