6

我编写了以下控制台应用程序来测试静态属性:

using System;

namespace StaticPropertyTest
{
    public abstract class BaseClass
    {
        public static int MyProperty { get; set; }
    }

    public class DerivedAlpha : BaseClass
    {
    }

    public class DerivedBeta : BaseClass
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            DerivedBeta.MyProperty = 7;
            Console.WriteLine(DerivedAlpha.MyProperty); // outputs 7
        }
    }
}

正如这个控制台应用程序所展示的,MyProperty对于 BaseClass 的所有实例,该属性只存在一次。是否有一种模式可以让我定义一个静态属性,该属性将为每个子类类型分配存储空间?

鉴于上面的示例,我希望 的所有实例DerivedAlpha共享相同的静态属性,并且所有实例DerivedBeta共享静态属性的另一个实例。

我为什么要这样做?

我正在懒惰地初始化具有某些属性的类属性名称集合(通过反射)。每个派生类实例的属性名称都是相同的,因此将其存储在每个类实例中似乎很浪费。我不能在基类中让它成为静态的,因为不同的子类会有不同的属性。

我不想在每个派生类中复制填充集合(通过反射)的代码。我知道一种可能的解决方案是在基类中定义填充集合的方法,并从每个派生类调用它,但这不是最优雅的解决方案。

更新 - 我在做什么的例子

应乔恩的要求,这是我正在尝试做的一个例子。基本上,我可以选择使用属性来装饰我的类中的[SalesRelationship(SalesRelationshipRule.DoNotInclude)]属性(还有其他属性,这只是一个简化的示例)。

public class BaseEntity
{
    // I want this property to be static but exist once per derived class.
    public List<string> PropertiesWithDoNotInclude { get; set; }

    public BaseEntity()
    {
        // Code here will populate PropertiesWithDoNotInclude with
        // all properties in class marked with
        // SalesRelationshipRule.DoNotInclude.
        //
        // I want this code to populate this property to run once per
        // derived class type, and be stored statically but per class type.
    }
}

public class FooEntity : BaseEntity
{
   [SalesRelationship(SalesRelationshipRule.DoNotInclude)]
   public int? Property_A { get; set; }

   public int? Property_B { get; set; }

   [SalesRelationship(SalesRelationshipRule.DoNotInclude)]
   public int? Property_C { get; set; }
}

public class BarEntity : BaseEntity
{
   public int? Property_D { get; set; }

   [SalesRelationship(SalesRelationshipRule.DoNotInclude)]
   public int? Property_E { get; set; }

   public int? Property_F { get; set; }
}

期望的最终结果

访问FooEntity.PropertiesWithDoNotInclude返回 a List<string>

{
  "Property_A",
  "Property_C"
}

访问BarEntity.PropertiesWithDoNotInclude返回 a List<string>

{
  "Property_E"
}
4

3 回答 3

4

两种可能的方法:

  • 使用属性;用属性装饰每个子类,例如

    [MyProperty(5)]
    public class DerivedAlpha
    {
    }
    
    [MyProperty(10)]
    public class DerivedBeta
    {
    }
    

    当然,这仅在它们有效地保持不变时才有效。

  • 使用字典:

    var properties = new Dictionary<Type, int>
    {
        { typeof(DerivedAlpha), 5) },
        { typeof(DerivedBeta), 10) },
    };
    

编辑:现在我们有了更多的上下文,Ben 的答案是一个非常好的答案,使用泛型在 C# 中的工作方式。它就像字典的例子,但是内置了惰性、线程安全和简单的全局访问。

于 2013-07-22T05:32:21.667 回答
3

Jon 像往常一样有一个很好的解决方案,尽管我看不出这里有什么好的属性,因为它们必须显式地添加到每个子类型中,而且它们的行为不像属性。

这种Dictionary方法绝对可行。这是另一种方法,它明确声明每个子类将有一个变量BaseEntity

class FilteredProperties<T> where T : BaseEntity
{
     static public List<string> Values { get; private set; }
     // or static public readonly List<string> Values = new List<string>();
     static FilteredProperties()
     {
         // logic to populate the list goes here
     }
}

这样做的缺点是很难与GetType()调用配对,例如您可能在BaseEntity. ADictionary或实现惰性人口的包装器更适合该用途。

于 2013-07-22T05:54:37.993 回答
0

我最近才需要同样的东西并遇到了这个问题。我认为 Jon 和 Fried 使用 a 的想法Dictionary是正确的,但并没有完全达到我想要的,所以我想我会展示我自己的完整且非常容易扩展的实现。

public class TypeStaticProperty<T>
{
    T _defaultValue;
    Dictionary<Type, T> _values = new Dictionary<Type, T>();

    public TypeStaticProperty(T defalutValue = default)
    {
        _defaultValue = defalutValue;
    }
    
    public T Get(object caller)
    {
        lock (_values)
        {
            if (_values.TryGetValue(caller?.GetType(), out T val))
                return val;
            else
                return _defaultValue;
        }
    }

    public void Set(object caller, T val)
    {
        lock (_values)
            _values[caller?.GetType()] = val;
    }
}

并证明:

class TestBaseClass
{
    static TypeStaticProperty<int> _property = new TypeStaticProperty<int>();
    public int Property
    {
        get => _property.Get(this);
        set => _property.Set(this, value);
    }
}

class TestClass1 : TestBaseClass
{
}

class TestClass2 : TestBaseClass
{
}

class Program
{
    static void Main(string[] args)
    {
        TestClass1 test1a = new TestClass1();
        TestClass1 test1b = new TestClass1();

        test1a.Property = 1;
        test1b.Property = 2;

        TestClass2 test2a = new TestClass2();
        TestClass2 test2b = new TestClass2();

        test2a.Property = 3;
        test2b.Property = 4;

        Console.WriteLine($"test1a.Property = {test1a.Property}");
        Console.WriteLine($"test1b.Property = {test1b.Property}");
        Console.WriteLine($"test2a.Property = {test2a.Property}");
        Console.WriteLine($"test2b.Property = {test2b.Property}");
    }
}

输出:

test1a.Property = 2
test1b.Property = 2
test2a.Property = 4
test2b.Property = 4

因此,虽然您仍然需要一个类实例来访问和设置属性,但该精确类型的所有实例的值将始终相同。(这也包括泛型;Foo<int>将被视为与 不同的类型Foo<string>)。与 Fried 的示例相比,这具有巨大的优势,因为您无需在编译时知道访问或设置时要查找的“静态”值的精确类型。

PS - 为了全面披露,这在很大程度上受到了 WPF 源代码的启发,该源代码使用非常相似的模式用于DependencyProperty's 和各种其他旨在提高性能和减少内存占用的内部花里胡哨。

于 2020-09-03T19:48:00.540 回答