【问题标题】:reference the "self" type in a static method - C#在静态方法中引用“self”类型 - C#
【发布时间】:2011-08-23 14:47:52
【问题描述】:

我正在尝试创建一个返回类实例的静态方法,例如:

class A {
   public static A getInstance() {
      return new A();
   }
}

我遇到的问题是,如果我有一个从 A 派生的子类 B,我希望 B.getInstance() 返回 B 的实例,而不是 A。在 PHP 世界中,您可以使用关键字“self " 来引用你自己的类型,所以你的 getInstance() 看起来像:

public static function getInstance() {
   return new self();
}

最好的方法是什么?

【问题讨论】:

  • 我曾经做过的唯一方法是使用 CRTP 通过泛型将派生类型传递给基类型。这里不受欢迎,所以我不再讨论它。也许反射和Activator.CreateInstance() 是你最好的选择?

标签: c#


【解决方案1】:

您可以隐藏等以某种方式覆盖继承类型中的 getInstance 方法,如下所示:

class B : A {
    public static new B getInstance() {
        return new B();
    }
}

new 关键字指定隐藏继承的方法是故意的,否则您会收到编译器警告。

完整示例:

using System;

class A {
    public static A getInstance() { return new A(); }
    public void PrintSelf() {Console.WriteLine(this.GetType().Name);}
}

class B : A {   
    public static new B getInstance() {
        return new B();
    }
}

public class MyClass {
    public static void Main() {
        A a = A.getInstance();
        B b = B.getInstance();
        a.PrintSelf();
        b.PrintSelf();
    }
}

【讨论】:

  • 这实际上可以满足我的目的。谢谢!
  • 请记住,如果您忘记在任何地方执行此操作,编译器不会警告您...您只会默默地被赋予“错误”类型。
【解决方案2】:

你不能,基本上。如果对仅在基类中声明的静态成员的调用实际上以派生类的形式表示,如下所示:

// Urgh
Encoding ascii = ASCIIEncoding.ASCII;

// Worse yet (and yes, I've seen this)
Encoding ascii = UTF8Encoding.ASCII;

然后编译器默默地将其转换为:

Encoding ascii = Encoding.ASCII;

原始源代码包含派生类的名称这一事实并未保留在编译代码中,因此无法在执行时对其进行操作。

【讨论】:

  • 有道理。 PHP 可以做到这一点,因为它是一种解释型语言 - 该功能实际上称为“后期静态绑定”(php.net/manual/en/language.oop5.late-static-bindings.php)。谢谢!
  • 我认为UTF8Encoding.Default 更糟糕。至少对于.ASCII,您应该期望它实际上是ASCII。使用.Default,它看起来像UTF-8 的默认变体,或者类似的东西。
  • @svick:是的。请注意,Default 的名称一开始就很糟糕 - 它应该是 SystemDefault 以将其与“用于大多数 API 调用的默认编码”(即 UTF-8)区分开来。
【解决方案3】:

静态没有办法——你只需要使用实际的名称。

不过,我敢打赌 PHP 会动态地(即通过反射)做到这一点——在这种情况下,您始终可以使用 GetCurrentMethod 来获取有关该方法及其封闭类型的信息。

这样你可以做这样的事情:

using System.Reflection;

class Test
{
    static object MakeTest()
    {
        return Activator.CreateInstance(
            MethodBase.GetCurrentMethod().DeclaringType);
    }

    void Hello() { Console.WriteLine("Hi!"); }

    static void Main()
    {
        dynamic test = MakeTest();
        test.Hello();
    }
}

【讨论】:

  • 我认为这不会起作用,因为DeclaringType 将是基类(因为存在静态方法)而不是派生类。
  • @siride:很好——我想我当时可能误解了这个问题。看起来 OP 正在寻找 Python 的类方法,而 C# 还没有。
  • 允许我这样做的 PHP 功能称为“后期静态绑定”-php.net/manual/en/language.oop5.late-static-bindings.php。显然,在 C# 世界中没有等价物。
【解决方案4】:

您不能覆盖派生类中的静态成员函数。如果选择静态方法,只需将相同的方法添加到 B 类即可。

当然,您也可以使用不同的设计模式,例如 Factory,但我认为这超出了问题的范围。

【讨论】:

    【解决方案5】:

    如何使用generics 提供要生成的类型:

    public static T getInstance<T>() where T : A, new()
    {
        return new T();
    }
    

    【讨论】:

      【解决方案6】:

      既然其他人说没有办法做到这一点,我就提供 CRTP。它有它的缺点(Eric Lippert 在某处有一篇关于它的帖子),但它会解决这个问题

      class Base<T> where T : Base<T>, new() {
          public static T GetInstance() {
              return new T();
          }
      }
      
      class Derived : Base<Derived> {
      }
      
      Derived d = Derived.GetInstance();
      

      但是,这确实使Base&lt;T&gt; 难以使用。 Base&lt;T&gt; 最好是抽象的。你也可以像这样创建一个可以实际实例化和使用的侧类:

      class Base : Base<Base> {
      }
      

      它看起来很奇怪,但它确实有效。不幸的是,BaseDerived 将不再相关(Base&lt;T&gt; 除外),因此您不能在它们之间进行转换。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-03
      • 2021-03-17
      相关资源
      最近更新 更多