我试图了解一段简化为以下代码段的代码:
abstract class A_base {}
abstract class A_derived : A_base {}
abstract class B_base {
protected virtual T Foo<T>() where T : A_base, new() {
return new T();
}
}
abstract class B_derived : B_base {
protected new virtual T Foo<T>() where T : A_derived, new() {
return base.Foo<T>();
}
}
具体来说,我试图了解new virtual
类中Foo
方法中B_derived
背后的原因。据我了解,作者想要收紧Foo
方法的通用参数(从A_base
到A_derived
),同时让来自B_derived
的类覆盖{ {1}}方法。
目前,在Foo
或Foo
B_base
的实施
还有其他原因吗?这样做有什么陷阱吗?这是在重写方法中限制类型参数的常用方法吗?或者这是一种令人费解的方式,有更好的方法吗?
答案 0 :(得分:1)
以下是原因:
[回答使用新]请注意Foo没有被覆盖。由于重写需要相同的方法签名(包括约束)。这不是这种情况,约束正在改变,从而有效地改变了整个签名。因此,采用了新的虚拟关键字。
[回答使用虚拟]现在作者想要将B_base.Foo()和B_derived.Foo视为不同的方法,如第1点所述,他还希望用户能够覆盖这些不同的方法因此需要使用Virtual关键字。
当需要更改方法的签名并且还希望允许覆盖每个单独方法的能力时,这是处理预期问题的常用方法。
答案 1 :(得分:1)
通过说new virtual
,您创建了一个隐藏原始方法的新方法(不会覆盖基类版本)。这意味着从类型为B_derived
的变量调用的B_derived
派生的类上调用的任何方法都将使用新方法签名(和功能)。这也意味着不可能覆盖原始文件,因为它已经从方法列表中有效地删除了。
为了有一个更具体的例子,我添加了一些类和一些日志记录。
abstract class A_base {}
abstract class A_derived : A_base {}
abstract class B_base {
public virtual T Foo<T>() where T : A_base, new() {
Console.Write("1");
return new T();
}
}
abstract class B_derived : B_base {
public new virtual T Foo<T>() where T : A_derived, new() {
Console.Write("2");
return base.Foo<T>();
}
}
class C1 : A_base {}
class C2 : A_derived {}
class D1 : B_base {
public override T Foo<T>() {
Console.Write("3");
return base.Foo<T>();
}
//override T Foo<T>() where T : A_derived, new() {
// //Error constraint mismatch
//}
}
class D2 : B_derived {
public override T Foo<T>() {
Console.Write("4");
return base.Foo<T>();
}
//override T Foo<T>() where T : A_base, new() {
// //Error constraint mismatch
//}
}
public class Program
{
public static int Main(string[] args) {
//Comments are what is printed out.
var d1 = new D1();
var d2 = new D2();
d1.Foo<C1>(); //31
Console.WriteLine ();
d1.Foo<C2>(); //31
Console.WriteLine ();
//d2.Foo<C1>(); //Error constraint mismatch
d2.Foo<C2>(); //421
Console.WriteLine ();
B_base b_d2 = d2;
b_d2.Foo<C1>(); //1
Console.WriteLine();
b_d2.Foo<C2>(); //1
Console.WriteLine ();
return 0;
}
}