C# 访问父级中子级的静态成员

C# access children's static member in parent

我有一个 class Foo,它是许多其他 class 的基础 class,例如 Bar 和 Baz,我想在 Foo 中使用Bar和Baz中的静态成员,如下图:

public class Foo{
    public result1 {
         get{
             return field1;
         }
    }
}

public class Bar : Foo{
    public const int field1 = 5;
}

public class Baz : Foo{
    public const int field1 = 10;
}

我能想到的唯一解决方案是将所有字段包装在一个容器中,为每个对象添加一个额外的标识符,然后使用函数 return 字段,就像这样

Bar : Foo{
    public readonly int id = 0;
    public static Wrapper wrapper;
}

public Wrapper GetWrapper(int id){
    switch(id){
        case 0:
            return Bar.wrapper;
    }
}

但是,如您所见,我需要维护一个额外的 class 和函数,我不想让我的代码碎片化。还有其他选择吗?

编辑

您所要求的,即从基 class 访问子 class 中的 staticconst 值在技术上是可行的,但这样做会违反良好 SOLID OO 设计的原则。此外,由于您将需要特定子 class 的实例,以便能够 'reason over' 子 class 的类型以获得适当的 field1,静态地解决这个问题毫无意义。

相反,这里更常见、更简洁的方法是使用 subtype polymorphicism,这将允许在基础 class 中调用方法,或在外部 class 中调用方法,根据 subclass 访问 'field1' 的适当值。这允许控制值 returned 保留在适当的 subclasses 中(即按照你的话,代码不会变成 "fragmented")。

使用subclass多态的替代方案(推荐)

子class 多态方法(即使用 virtual/abstractoverride 关键字)将允许您封装可针对每个自定义的值(或对象)的检索子class。在这里,抽象在概念上保持在 "give me an integer value",然后 'how' 到 return 的子 class 特定实现可以从调用者那里抽象(隐藏)值。此外,通过将基础 属性 标记为 abstract,您将强制所有子 class 实现 属性,这样就不会忘记提供值的要求关于。

即我会推荐这样的多态方法:

public abstract class Foo
{
    public abstract int Result { get;  }
}

public class Bar : Foo
{
    // This is implementation specific. Hide it.
    private const int field1 = 5;

    public override int Result 
    { 
        get { return field1; }
    }
}

public class Baz : Foo
{
    public override int Result 
    { 
        // No need for this implementation to be a constant ...
        get { return TheResultOfAReallyComplexCalculationHere(); }
    }
}

如果基础上没有其他可重用的具体方法class Foo,那么您也可以将抽象建模为接口,效果相同:

public interface IFoo
{
    int Result { get;  }
}

不用多态性解决这个问题(不推荐)

任何编译时尝试访问 subclasses 上的静态字段通常需要代码在某处切换(或映射)subclass 实例的实际类型,例如:

public class Foo
{
    public int result1 
    {
         get
         {
             switch(this.GetType().Name)
             {
                case "Bar":
                    return Bar.field1;
                case "Baz":
                    return Baz.field1;
                default:
                    return 0;
             }
         }
    }

    public void MethodRequiringValueFromSubclass()
    {
        Console.WriteLine(result1);
    }
}

public class Bar : Foo
{
    public const int field1 = 5;
}

public class Baz : Foo
{
    public const int field1 = 10;
}

这里的问题是违反了 Open and Closed principal,因为每次添加新的子 class 时,都需要更改 result1 方法以适应新的 class.

我建议使用抽象函数而不是静态成员。

public abstract class Foo{
    public result1 {
         get{
             return get_field1();
         }
    }
    protected abstract int get_field1();
}

public class Bar : Foo{
    public const int field1 = 5;
    protected override int get_field1() { return field1;}
}

public class Baz : Foo{
    public const int field1 = 10;
    protected override int get_field1() { return field1;}
}

您可以将构造函数参数添加到您的 Foo class 中,它可以从继承者那里传递,因此您不需要额外的 classes 也可以减少耦合

public class Foo
{
    private readonly int _field1;
    public Foo(int field1)
    {
         _field1 = field1;
    }
}

或者您可以完全从继承类型使用它,因为 static/const 成员是 class 类型

的成员
public class Foo
{
    public result1 
    {
         get
         {
             return Bar.field1;
         }
    }
}

但这会降低您的代码的灵活性和耦合度。

您还可以选择使用虚拟属性,您可以在派生 classes 中实现并在基础中使用:

public class Foo
{
    public virtual int Field { get { return 0; } }
}

您可以使用 virtual 并在每个子 class

中覆盖 result1,而不是像其他答案建议的那样使 Foo 抽象
public class Foo
{
    public virtual int result1 { get; }
}

public class Bar : Foo
{
    public const int field1 = 5;

    public override int result1
    {
        get { return field1; }
    }
}

public class Baz : Foo
{
    public const int field1 = 10;

    public override int result1
    {
        get { return field1; }
    }
}

如果你想默认 result1 到 return 除了 0 你可以给它另一个值

public class Foo
{
    public virtual int result1 { get; } = -1;
}

当我回答自己的问题时,我总是觉得自己像个混蛋...但没有看到我所期待的,所以我不妨分享一下经过一夜的思考后得到的结果。

我不想计算abstract/virtual的原因是因为有很多子class,而且所有子class的公式都是一样的。我只是拒绝重复输入相同的代码 10-20 次。

也不能使静态字段成为非静态字段,因为它们应该可以在 class 级别访问,而且它们可以变大,并且它们对所有实例都是相同的。

我能想到的最小化代码片段的唯一解决方案是这样的

public class Foo {
    public class Wrapper {
        Fields...
    }
    public Wrapper wrapper; // reference
    public int result1 { get; }
}

public class Bar : Foo {
    public static Wrapper subclassWrapper; // put in the implementation
    public Bar() : base(){
        wrapper = subclassWrapper;
    }
}

所以现在每个实例都需要保存一个额外的引用,但是我不需要保留一个函数。包装器保留在基础 class 中,因此碎片较少。