关于c#:如何使用interface或abstract确保类具有静态属性?

How can I assure a class to have a static property by using interface or abstract?

我有一个抽象类,比如mybase。我希望从MyBase派生的所有类都有一个静态字段

1
2
3
4
public static List<string> MyPArameterNames
{
get {return _myParameterNames;}
}

所以,每个子类都可以告诉它使用什么参数名;我想要静态的,因为我不想为此创建实例。

我怎样才能做到这一点?


你不能那样做。接口、抽象等不能应用于静态成员。如果您想要完成这一点,您必须手动记住在所有派生类上都要这样做。

此外,静态成员由派生类继承。如果子类希望指定备用行为,则它们必须隐藏静态父成员。


无论如何,这是没有意义的,因为在不确定类型的情况下,您将无法访问该静态属性,从而破坏了拥有接口的整个意义。

我只需在接口上放置一个属性,并将其路由到静态成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IMyInterface
{
    void Foo();
    IList<string> Properties { get; }
}

public class ConcreteClass : IMyInterface
{
    public void Foo(){}
    public IList<string> Properties
    {
        get { return s_properties; }
    }
}

但这让我想到了第二个问题——你想完成什么?为什么需要在类上有一个静态成员?你真正想要的是,给定一个对象,能够确定它有什么属性,对吗?那么,为什么您的代码会关心它们是静态存储的还是每个实例存储的呢?

似乎您将合同(您希望能够做的事情)与实现(服务提供者如何实现目标)混淆了。


好啊。也许我不够清楚。但我基本上已经通过这样的方式实现了我所需要的:

1
2
3
4
5
6
7
8
9
10
11
12
public abstract myBaseClass
{
 public List<string> MyParameterNames
   {
     get
         {
             throw
               new ApplicationException("MyParameterNames in base class
                                 is not hidden by its child."
);
         }
   }
}

因此,如果试图使用MyParameterNames属性访问派生类的参数名,则从此类派生的任何类都将引发异常。

不是一个完美的方法,但它在某种程度上帮助我克服了我的问题。


解决方案的所有部分都在这里,分布在多个答案中。

  • 像通常那样创建接口。
  • 创建一个实现接口的抽象基类,并定义所有需要的静态成员。
  • 从抽象基类继承,而不是在创建实际实现时从接口继承。
  • 虽然它仍然不允许您从abstractClass.myParameterNames访问subclass.myParameterNames,但是您将能够确保abstractClass的所有实现都具有该属性。

    但是,根据用例的具体情况,最好将MyParameterNames公开为非静态成员,并简单地将其实现为单例,以便每个子类只有一个列表副本。不管怎样,您仍然需要初始化类的一个实例,以便获得所需的数据。

    至少,要获取静态数据,您需要知道您正在处理的特定子类,因此尝试从接口(可以是任意的、未知的数据类型)查看它没有多大意义。


    虽然接口上不可能有static值,但抽象类上可能有静态值。不过,这个实例是在抽象类的级别上保存的;因此对于所有派生类都是通用的。根据您的需要,您可以利用它;即在您的基类上有一个字典,其键是一个类型(类型是派生类的类型),然后将您的列表保存在该类型下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    //example of the base class
    public abstract class MyAbstractBaseClass
    {
        private static readonly IDictionary<Type,IList<MyAbstractBaseClass>> values = new Dictionary<Type,IList<MyAbstractBaseClass>>();
        public List<string> MyParameterNames
        {
            get
            {
                return values[this.GetType()].Select(x => x.Name).ToList();
            }
        }
        public string Name {get; private set;}
        protected MyAbstractBaseClass(string name)
        {
            //assign the new item's name to the variable
            Name = name;
            //keep a list of all derivations of this class
            var key = this.GetType();
            if (!values.ContainsKey(key))
            {
                values.Add(key, new List<MyAbstractBaseClass>());
            }
            values[key].Add(this);
        }
    }

    //examples of dervived class implementations
    public class MyDerivedClassOne: MyAbstractBaseClass
    {
        private MyDerivedClassOne(string name): base(name){}
        public static readonly MyDerivedClassOne Example1 = new MyDerivedClassOne("First Example");
        public static readonly MyDerivedClassOne Example2 = new MyDerivedClassOne("Second Example");
    }
    public class MyDerivedClassTwo: MyAbstractBaseClass
    {
        private MyDerivedClassTwo(string name): base(name){}
        public static readonly MyDerivedClassTwo Example1 = new MyDerivedClassTwo("1st Example");
        public static readonly MyDerivedClassTwo Example2 = new MyDerivedClassTwo("2nd Example");
    }

    //working example
    void Main()
    {
        foreach (var s in MyDerivedClassOne.Example1.MyParameterNames)
        {
            Console.WriteLine($"MyDerivedClassOne.Example1.MyParameterNames: {s}.");
        }
        foreach (var s in MyDerivedClassTwo.Example1.MyParameterNames)
        {
            Console.WriteLine($"MyDerivedClassTwo.Example1.MyParameterNames: {s}.");
        }
    }

    它与拥有静态属性不完全相同(例如,在没有首先创建实例的情况下,不能简单地访问属性),但在某些用例中,它可能工作得很好。


    为什么不使make myparameternames虚拟化,并在派生类中重写它们以引发异常?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public abstract class BaseClass
    {
        public virtual List<string> MyParameterNames
        {
            get;
        }
    }

    public class DerivedClass : BaseClass
    {
        public override List<string> MyParameterNames
        {
            get
            {
                throw new Exception();
            }
        }
    }


    MyBase的构造函数中,可以调用GetType()并使用反射来确保派生类具有正确的属性。显然,这只会在运行时得到,但我不确定这个约束的意义:如果静态属性不在那里,有什么坏处?


    这是不可能的。继承不能应用于类型的成员(静态成员)。