为什么静态虚拟是不可能的?是 C# 依赖还是在 OO 世界中没有任何意义?
我知道这个概念已经被强调了,但我没有找到上一个问题的简单答案。
virtual
表示将在运行时选择调用的方法,具体取决于对象的动态类型。static
表示不需要任何对象来调用该方法。
你建议如何以相同的方法来做这两者?
Eric Lippert 有一篇关于此的博客文章,并且像往常一样在他的文章中,他非常深入地涵盖了这个主题:
“虚拟”和“静态”是对立的!“virtual”表示“根据运行时类型信息确定要调用的方法”,“static”表示“仅根据编译时静态分析确定要调用的方法”</p>
“静态”与“虚拟”的矛盾只是C#的问题。如果“静态”被“类级别”取代,就像在许多其他语言中一样,没有人会被蒙住眼睛。
太糟糕了,单词的选择使 C# 在这方面瘫痪了。仍然可以调用 Type.InvokeMember 方法来模拟对类级别的虚拟方法的调用。您只需将方法名称作为字符串传递。没有编译时检查,没有强类型,也没有控制子类实现该方法。
一些德尔福美女:
type
TFormClass = class of TForm;
var
formClass: TFormClass;
myForm: TForm;
begin
...
formClass = GetAnyFormClassYouWouldLike;
myForm = formClass.Create(nil);
myForm.Show;
end
那些说静态虚拟方法没有意义的人——如果你不明白这怎么可能,那并不意味着它是不可能的。有些语言允许这样做!以德尔福为例。
我会成为那个反对的人。您所描述的内容在技术上不是该语言的一部分。对不起。但是可以在语言中模拟它。
让我们考虑一下您的要求 - 您想要一组不附加到任何特定对象的方法,这些方法都可以在运行时或编译时轻松调用和替换。
对我来说,这听起来像是你真正想要的是一个带有委托方法的单例对象。
让我们一起举个例子:
public interface ICurrencyWriter {
string Write(int i);
string Write(float f);
}
public class DelegatedCurrencyWriter : ICurrencyWriter {
public DelegatedCurrencyWriter()
{
IntWriter = i => i.ToString();
FloatWriter = f => f.ToString();
}
public string Write(int i) { return IntWriter(i); }
public string Write(float f) { return FloatWriter(f); }
public Func<int, string> IntWriter { get; set; }
public Func<float, string> FloatWriter { get; set; }
}
public class SingletonCurrencyWriter {
public static DelegatedCurrencyWriter Writer {
get {
if (_writer == null)
_writer = new DelegatedCurrencyWriter();
return _writer;
}
}
}
正在使用:
Console.WriteLine(SingletonCurrencyWriter.Writer.Write(400.0f); // 400.0
SingletonCurrencyWriter.Writer.FloatWriter = f => String.Format("{0} bucks and {1} little pennies.", (int)f, (int)(f * 100));
Console.WriteLine(SingletonCurrencyWriter.Writer.Write(400.0f); // 400 bucks and 0 little pennies
鉴于这一切,我们现在有一个写出货币值的单例类,我可以改变它的行为。我基本上在编译时定义了行为约定,现在可以在编译时(在构造函数中)或运行时更改行为,也就是说,我相信你想要得到的效果。如果你想继承行为,你可以通过实现反向链接来做到这一点(即,让新方法调用前一个方法)。
也就是说,我并不特别推荐上面的示例代码。一方面,它不是线程安全的,而且真的没有很多东西可以让生活保持清醒。全球对这种结构的依赖意味着全球不稳定。这是在 C 的昏暗时期实现可变行为的众多方式之一:函数指针的结构,在这种情况下是单个全局结构。
对的,这是可能的。
最想要的用例是拥有可以“覆盖”的工厂
为此,您将不得不依赖使用F 有界多态性的泛型类型参数。
示例 1 我们以工厂为例:
class A: { public static A Create(int number) { return ... ;} }
class B: A { /* How to override the static Create method to return B? */}
您还希望createB
可访问并返回 B 类中的 B 对象。或者您可能希望 A 的静态函数成为 B 可以扩展的库。解决方案:
class A<T> where T: A<T> { public static T Create(int number) { return ...; } }
class B: A<B> { /* no create function */ }
B theb = B.Create(2); // Perfectly fine.
A thea = A.Create(0); // Here as well
示例 2(高级): 让我们定义一个静态函数来将值矩阵相乘。
public abstract class Value<T> where T : Value<T> {
//This method is static but by subclassing T we can use virtual methods.
public static Matrix<T> MultiplyMatrix(Matrix<T> m1, Matrix<T> m2) {
return // Code to multiply two matrices using add and multiply;
}
public abstract T multiply(T other);
public abstract T add(T other);
public abstract T opposed();
public T minus(T other) {
return this.add(other.opposed());
}
}
// Abstract override
public abstract class Number<T> : Value<T> where T: Number<T> {
protected double real;
/// Note: The use of MultiplyMatrix returns a Matrix of Number here.
public Matrix<T> timesVector(List<T> vector) {
return MultiplyMatrix(new Matrix<T>() {this as T}, new Matrix<T>(vector));
}
}
public class ComplexNumber : Number<ComplexNumber> {
protected double imag;
/// Note: The use of MultiplyMatrix returns a Matrix of ComplexNumber here.
}
现在您也可以使用静态MultiplyMatrix
方法直接从 ComplexNumber 返回一个复数矩阵
Matrix<ComplexNumber> result = ComplexNumber.MultiplyMatrix(matrix1, matrix2);
虽然从技术上讲,不可能定义静态虚拟方法,但由于此处已经指出的所有原因,您可以在功能上完成我认为您正在尝试使用 C# 扩展方法的功能。
从微软文档:
扩展方法使您能够将方法“添加”到现有类型,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。
查看扩展方法(C# 编程指南)了解更多详细信息。
在 .NET 中,虚拟方法分派(大致)是通过在运行时调用方法时查看对象的实际类型并从类的 vtable 中找到最重写的方法来完成的。调用静态类时,没有要检查的对象实例,因此没有要查找的 vtable。
总结所有提供的选项:
这不是 C# 的一部分,因为它static
意味着“在运行时不绑定到任何东西”,因为它自 C 以来(可能更早)就一直如此。static
实体绑定到声明类型(因此能够访问其其他static
实体),但仅在编译时。
static
等价物(如果需要的话)意味着“在运行时绑定到类型对象”。示例包括 Delphi、Python、PHP。这可以通过多种方式进行模拟,可分为以下几类: