【问题标题】:C# virtual static methodC# 虚拟静态方法
【发布时间】:2010-11-17 15:32:51
【问题描述】:

为什么静态虚拟是不可能的?是 C# 依赖还是在 OO 世界中没有任何意义?

我知道这个概念已经加了下划线,但我没有找到上一个问题的简单答案。

【问题讨论】:

标签: c# generics static methods virtual


【解决方案1】:

虽然从技术上讲,不可能定义 静态虚拟 方法,但由于此处已经指出的所有原因,您可以在功能上完成我认为您正在尝试使用 C# 扩展方法的功能。

来自微软文档:

扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。

查看Extension Methods (C# Programming Guide)了解更多详情。

【讨论】:

  • 扩展方法与普通虚拟方法没有区别。
【解决方案2】:

那些说静态虚拟方法没有意义的人 - 如果你不明白这怎么可能,这并不意味着它是不可能的。有些语言允许这样做!以 Delphi 为例。

【讨论】:

    【解决方案3】:

    “静态”与“虚拟”的矛盾只是C#的问题。如果“静态”被“类级别”取代,就像在许多其他语言中一样,没有人会被蒙上眼睛。

    太糟糕了,单词的选择使 C# 在这方面瘫痪了。仍然可以调用 Type.InvokeMember 方法来模拟对类级别的虚拟方法的调用。您只需将方法名称作为字符串传递。没有编译时检查,没有强类型,也没有控制子类实现该方法。

    一些德尔福美女:

    type
      TFormClass = class of TForm;
    var
      formClass: TFormClass;
      myForm: TForm;
    begin
      ...
      formClass = GetAnyFormClassYouWouldLike;
      myForm = formClass.Create(nil);
      myForm.Show;
    end
    

    【讨论】:

    • 这不是“错误的选择词”,static 的定义 - “仅根据编译时静态分析确定要调用的方法” 按照@ 987654321@ - 自从它在 C 中引入以来它的实际含义。功能请求实际上是将其含义更改为“类绑定”。
    【解决方案4】:

    Eric Lippert 有一篇关于此的博文,并且像往常一样在他的博文中深入探讨了这个主题:

    https://docs.microsoft.com/en-us/archive/blogs/ericlippert/calling-static-methods-on-type-parameters-is-illegal-part-one

    “虚拟”和“静态”是对立的! “virtual”表示“根据运行时类型信息确定要调用的方法”,“static”表示“仅根据编译时静态分析确定要调用的方法”

    【讨论】:

    • 投反对票,因为其他 OO 语言(尤其是 Delphi)实际上支持此功能。
    【解决方案5】:

    总结所有提供的选项:

    【讨论】:

      【解决方案6】:

      是的,这是可能的。

      最想要的用例是拥有可以“覆盖”的工厂

      为此,您将不得不依赖使用F-bounded polymorphism 的泛型类型参数。

      示例 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);
      

      【讨论】:

      • 这个其实叫“Curiously recurring template pattern”。
      • 这个名字是 1995 年一位工程师在 F 有界多态性被数学形式化至少 6 年后发明的。 bit.ly/1Ft54Ah当时还没有网络,不能怪他没看(谷歌成立于1999年)
      • 哇,我不知道。将此添加到维基百科文章中。
      • 刚刚将示例 1 应用于我的工具,避免了大量的样板代码。我遇到的一个小陷阱是对“Create”方法的调用。这必须称为 A.Create(0) 或 B.Create(2);普通的 A.Create(0) 或 B.Create(2) 不起作用。
      【解决方案7】:

      我将成为反对者。您所描述的内容在技术上不是该语言的一部分。对不起。但是可以在语言中模拟它

      让我们考虑一下您的要求 - 您想要一个不附加到任何特定对象的方法集合,这些方法都可以在运行时或编译时轻松调用和替换。

      在我看来,您真正想要的是一个带有委托方法的单例对象。

      我们举个例子:

      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
      

      鉴于这一切,我们现在有一个写出货币值的单例类,我可以改变它的行为。我基本上在编译时定义了行为convention,现在可以在编译时(在构造函数中)或运行时更改行为,也就是说,我相信你想要得到的效果.如果你想继承行为,你可以通过实现反向链接来做到这一点(即,让新方法调用前一个方法)。

      也就是说,我不特别推荐上面的示例代码。一方面,它不是线程安全的,而且真的没有很多地方可以让生活保持清醒。全球对这种结构的依赖意味着全球不稳定。这是在 C 的昏暗时期实现可变行为的众多方式之一:函数指针的结构,在这种情况下是单个全局结构。

      【讨论】:

        【解决方案8】:

        在 .NET 中,虚拟方法分派(大致)是通过在运行时调用方法时查看对象的实际类型并从类的 vtable 中找到最重写的方法来完成的。调用静态类时,没有要检查的对象实例,因此也没有要查找的 vtable。

        【讨论】:

          【解决方案9】:

          virtual 表示将在运行时选择调用的方法,具体取决于对象的动态类型。 static 表示调用该方法不需要对象。

          您建议如何以相同的方法来做这两者?

          【讨论】:

          • 我们不称它们为函数 :) 我们称它们为方法
          • @Yassir:啊,谢谢。把我当作这里的 C++ 人。 :) 我会纠正这个。
          • 我想做这样的事情:((I)typeof(mybject)).MyStaticFunction(与我,一个接口与MyStaticFunction接口的一个静态函数)我知道语法不正确但在这里是重点。
          • 自 1990 年代以来,Delphi 就有了虚拟类成员(又名虚拟静态)的概念。由于 Delphi 是由 Anders Hejlsberg & Co 早在 1990 年代创建的,因此很自然地引出了他为什么从未将它(或类似的东西)引入 C# 的问题。是的,在讨论构造函数时它会使问题复杂化,但我相信存在一个优雅的解决方案。 +1 给 OP
          • @sbi, docwiki.embarcadero.com/RADStudio/en/… 有很多有效的场景,请在 Embarcadero 论坛上发帖请求一些示例。
          猜你喜欢
          • 1970-01-01
          • 2012-10-11
          • 2011-02-12
          • 1970-01-01
          • 1970-01-01
          • 2010-12-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多