【问题标题】:C#, implement 'static abstract' like methodsC#,实现类似方法的“静态抽象”
【发布时间】:2010-10-23 20:18:19
【问题描述】:

我最近遇到了一个问题,我似乎需要一个“静态抽象”方法。我知道为什么这是不可能的,但是我该如何解决这个限制呢?

例如,我有一个包含描述字符串的抽象类。由于此字符串对所有实例都是通用的,因此将其标记为静态,但我想要求从该类派生的所有类都提供自己的 Description 属性,因此我将其标记为抽象:

abstract class AbstractBase
{
    ...
    public static abstract string Description{get;}
    ...
}

它当然不会编译。我想过使用接口,但接口可能不包含静态方法签名。

我应该让它简单地成为非静态的,并且总是获取一个实例来获取该类的特定信息吗?

有什么想法吗?

【问题讨论】:

标签: c# static abstract


【解决方案1】:

你不能。

做这件事的地方是属性。

例如

[Name("FooClass")]
class Foo
{
}

【讨论】:

  • 确实......可以让公共静态描述属性获取属性值,但使其更易于访问......
  • 有兴趣知道它在运行时是如何工作的。该属性是否以某种方式最终成为实例变量?我之前通过反射阅读过自定义属性,但不知道 CLR 是如何处理它们的
  • 您可以对对象执行 GetType 并通过反射获取装饰该类型的自定义属性列表。每个自定义属性都可以有自己的状态,可以通过属性公开。
  • 这样反射不会很贵吗?
  • @Davy8:这可能是不可能的,但你总是可以编写一个单元测试来断言Foo 的所有子类型都应用了NameAttribute
【解决方案2】:

如果您不介意推迟实现以明智地实现 Description 属性,您可以这样做

public abstract string ClassDescription {get; } 
// ClassDescription is more intention-revealing than Description

实现类会做这样的事情:

static string classDescription="My Description for this class";
override string  ClassDescription { get { return classDescription; } }

然后,您的班级必须遵守有描述的合同,但您让他们明智地去做。没有办法以面向对象的方式指定实现(除非通过残忍、脆弱的黑客攻击)。

但是,在我看来,这个描述是类元数据,所以我更喜欢使用其他人描述的属性机制。如果您特别担心反射的多种用途,请创建一个反射您关注的属性的对象,并在类型和描述之间存储一个字典。这将最大限度地减少反射(除了运行时类型检查,这并不是那么糟糕)。字典可以存储为通常需要此信息的任何类的成员,或者,如果跨域的客户端需要它,则可以通过单例或上下文对象存储。

【讨论】:

    【解决方案3】:

    将静态和抽象结合起来有点毫无意义,是的。静态背后的想法是不需要提供类的实例来使用相关成员;但是对于抽象,人们期望实例是提供具体实现的派生类。

    我明白你为什么想要这种组合,但事实是唯一的效果是拒绝实现使用“this”或任何非静态成员。也就是说,父类将在派生类的实现中规定一个限制,即使调用抽象或“静态抽象”成员之间没有根本区别(因为两者都需要一个具体实例来确定要使用的实现)

    【讨论】:

    • 我又想到了这种情况,我说那是真的...... public abstract string {get;} 像 public string {get {return "Izeeeeeeeeh";}} 一样实现了足够的类特定。
    • 这不是没有意义的。想象一下,您正在设计一个框架。您要求实现 ISomething 的类公开一些编译时已知数据。例如“友好的类名”。您希望编译器确保所有 ISomething 都具有此属性。如果你现在想这样做,那么你必须使用实例属性。这意味着创建一个实例,而不仅仅是查询类型。太糟糕了。
    • 我同意@RyanBarrett,对于框架设计,强制消费者在派生类中提供函数可能很有用,但也可以通过合同声明该函数是静态执行的,没有实例.
    • 另一种方法是允许一个抽象基类声明它必须在每个子类上定义一个特定的属性(再次提供编译时间常数数据)
    【解决方案4】:

    如果它是静态的,那么只有一个变量实例,如果我们可以在派生类中使用静态变量完成您想要完成的事情,我看不出继承会有什么意义。就我个人而言,我认为您会尽量避免使用实例变量。

    为什么不只是经典的方式?

    abstract class AbstractBase
    {
        protected string _Description = "I am boring abstract default value";
    }
    
    class Foo : AbstractBase {
    
         public Foo() {
           _Description = "I am foo!";
         }
    }
    

    【讨论】:

      【解决方案5】:

      一种可能的解决方法是在泛型的帮助下在基类中定义派生类的单例。

      import System;
      
      public abstract class AbstractBase<T>
          where T : AbstractBase<T>, new()
      {
          private static T _instance = new T();
      
          public abstract string Description { get; }
          public static string GetDescription()
          {
              return _instance.Description;
          }
      }
      
      public class DerivedClass : AbstractBase<DerivedClass>
      {
          public override string Description => "This is the derived Class";
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine(DerivedClass.GetDescription());
              Console.ReadKey();
          }
      }
      

      诀窍是告诉你的AbstractBase&lt;T&gt; 一些关于DerivedClass 是如何实现的细节:

      • where T: new() 是新的,所以它可以创建一个 Singleton 实例
      • 它以where T : AbstractBase&lt;T&gt; 派生自自身,因此它知道会有Description 的实现

      这样_instance 包含Description 字段,可以在静态方法GetDescription() 中调用。 这会强制您在 DerivedClass 中覆盖 Description,并允许您使用 DerivedClass.GetDescription() 调用其值

      【讨论】:

        【解决方案6】:

        如果必须在实例上调用它,则它不是静态的。

        如果您没有在实例上调用它,那么就没有多态性在起作用(即就语言而言,ChildA.Description 与 ChildB.Description 完全无关)。

        【讨论】:

          【解决方案7】:

          你可以...

          在抽象类中...

           protected abstract InWindow WindowInstance { get; set; }
          

          在派生类中...

              private static InWindow _instance;
          
          
              protected override InWindow WindowInstance
              {
                  get => _instance;
                  set => _instance = value;
              }
          

          【讨论】:

          • 这应该是你能得到的最接近的。只需使用非静态属性包装您的静态字段。
          【解决方案8】:

          您可以让“抽象”基方法抛出Exception,因此如果开发人员试图在子类上调用此方法而不进行覆盖,则会被“警告”。

          缺点是可能会扩展类而不使用此方法。然后参考提供的其他答案。

          【讨论】:

            猜你喜欢
            • 2014-04-25
            • 1970-01-01
            • 1970-01-01
            • 2012-12-03
            • 2021-01-10
            • 2013-08-02
            • 2011-03-19
            • 1970-01-01
            • 2011-09-26
            相关资源
            最近更新 更多