定义每个子类定义一次的静态属性的最佳方法是什么?

时间:2013-07-22 05:25:02

标签: c# .net reflection static

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

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的所有实例共享静态属性的另一个实例

为什么我要尝试这样做?

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

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

更新 - 我正在做的事情

在Jon的要求下,这是我正在尝试做的一个例子。基本上,我可以选择使用[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会返回List<string>

{
  "Property_A",
  "Property_C"
}

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

{
  "Property_E"
}

4 个答案:

答案 0 :(得分: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#中的工作方式。它就像字典示例,但内置了懒惰,线程安全和简单的全局访问。

答案 1 :(得分:4)

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的方法中使用。实现懒惰人口的Dictionary或其包装器更适合这种用途。

答案 2 :(得分:0)

答案一: 使用 new int

覆盖每个类中的属性
public abstract class BaseClass
{
    public static int MyProperty { get; set; }
}

public class DerivedAlpha : BaseClass
{
    public static new int MyProperty { get; set; }  // !! new int !!
}

public class DerivedBeta : BaseClass
{
    public static new int MyProperty { get; set; } // !! new int !!
}

        DerivedAlpha.MyProperty = 7;
        DerivedBeta.MyProperty = 8;
        BaseClass.MyProperty = 9;

答案二:
将值放在每种类型的字典中

public abstract class BaseClass2<T>
{
    public static Dictionary<Type, int> propertyStore = new Dictionary<Type, int>();

    public static int MyProperty
    {
        get
        {
            var t = typeof(T);
            return propertyStore[t];
        }

        set
        {
            var t = typeof(T);
            propertyStore[t] = value;
        }
    }
}

public class DerivedAlpha2 : BaseClass2<DerivedAlpha2>
{
}

public class DerivedBeta2 : BaseClass2<DerivedBeta2>
{
}

        DerivedBeta2.MyProperty = 7;
        DerivedAlpha2.MyProperty = 8;
        // BaseClass2.MyProperty = 9;  // does not compile 

答案 3 :(得分:0)

我最近才需要同样的东西,并且遇到了这个问题。我认为,乔恩(Jon)和弗里德(Fried)的使用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源代码的启发,WPF源代码对DependencyProperty和所有其他旨在改善性能并减少内存占用的内部钟声使用非常相似的模式