【问题标题】:Automatically create a copy of a static variable for each derived class为每个派生类自动创建静态变量的副本
【发布时间】:2016-03-27 21:31:11
【问题描述】:

我有一个带有静态变量的基类。我想创建派生子类,它们将自动拥有自己的非共享静态变量。理想情况下应该是这样的:

class Parent
{
     Texture2D picture;
     static Texture2D pictureOrigin;

     Parent()
     {
         picture = pictureOrigin;
         /*Loading the static origin to an instance variable
         because I might want to have more pictureOrigins 
         and alternate them or perhaps change the picture
         of the instance based on a certain event, etc.*/            
     }
}

class Subclass1 : Parent
{
    Subclass1() : base()
    { }
}

class Subclass2 : Parent
{
    Subclass2() : base()
    { }
}

void main()
{
    Parent.pictureOrigin = Load("pictureForParent"); 
    Subclass1.pictureOrigin = Load("pictureForSubclass1");
    Subclass2.pictureOrigin = Load("pictureForSubclass2");
    //Then creating instances of the classes and drawing them, etc.
}

但是发生的情况是它们都获得了最后加载的图像 (pictureForSubclass2),因为静态变量 pictureOrigin 在它们之间共享。

最快的解决方法是手动为每个子类添加新的静态变量pictureOrigin,并隐藏基类的pictureOrigin变量:

class Subclass1 : Parent
{
    new static Texture2D pictureOrigin;

    Subclass1() : base()
    {
         picture = pictureOrigin;
    }
}

或者,创建抽象方法或类似方法以确保在子类中创建新的静态变量。但这似乎太麻烦而且不太优雅。有更好的方法吗?

【问题讨论】:

  • 静态似乎是个坏主意,为什么它必须是静态的?您可以将图像资源加载到某个图像存储中,然后为每个实例提供所需的图像?
  • 在您的代码中,Subclass1 和 Subclass2 不是从 Parent 继承的。
  • 您可以将Parent 设为通用,如class Parent<T> where T : Parent<T>,然后为继承指定子类的类型:class Subclass1 : Parent<Subclass1>。然后,您将利用 <T> 类型的每个唯一组合将获得所有静态字段的自己的副本这一事实。话虽如此,我会尝试完全摆脱 static 关键字并找到一种不同的方法来做到这一点。

标签: c# inheritance static-variables


【解决方案1】:

在泛型类型中声明的静态成员基本上是按类及其泛型声明的。 例如Foo<Bar>.baz 不等于Foo<Qux>.baz

所以基本上你可以这样做:

abstract class Parent<T>
{
     Texture2D picture;
     static Texture2D pictureOrigin;

     Parent()
     {
         picture = pictureOrigin;
         /*Loading the static origin to an instance variable
         because I might want to have more pictureOrigins 
         and alternate them or perhaps change the picture
         of the instance based on a certain event, etc.*/            
     }
}

class Parent : Parent<Parent>
{
    Parent () : base()
    { }
}

class Subclass1 : Parent<Subclass1>
{
    Subclass1() : base()
    { }
}

class Subclass2 : Parent<Subclass2>
{
    Subclass2() : base()
    { }
}

void main()
{
    Parent.pictureOrigin = Load("pictureForParent"); 
    Parent<Subclass1>.pictureOrigin = Load("pictureForSubclass1");
    Parent<Subclass2>.pictureOrigin = Load("pictureForSubclass2");
}

【讨论】:

    【解决方案2】:

    你的问题闻起来像是糟糕的设计。在我看来,静态变量通常是不好的做法,正确的面向对象设计可以消除使用静态成员的需要。

    尝试像这样重构:

    public class Parent
    {
        private Texture2D texture;
    
        public Parent(Texture2D texture) {
            this.texture = texture;
        }
    
        public Texture2D Picture { get {
                return texture;
            }
        }
    }
    
    public class SubClass1 : Parent
    {
        public SubClass1(Texture2D texture) : base(texture) {
    
        }
    }
    

    让我详细说明为什么静态是一个糟糕的选择:

    1. 您的类现在只适用于单个位图。消除了为多个位图重用一个类的可能性(这是您要克服的限制)
    2. 在调用静态设置器之前,您的类不处于有效状态。通常,对象一旦构建,就应该处于有效状态。如果其他人正在使用您的对象,他们不会很明显地意识到他们必须在类上静态设置位图。
    3. 中断自然垃圾收集。例如,如果您希望在收集 SubClass 的所有实例时对 Texture2D 对象进行垃圾收集,则它不适用于您的静态设计。或者,如果您使用 oop 设计(如建议的那样),您可以根据您的用例灵活地进行垃圾收集或不收集。
    4. 使线程更加复杂。静态是全局的,因此您需要全局互斥锁来保证线程安全。
    5. 使测试更加困难。如果你想对这个类进行单元测试,你必须确保在每次测试后都清理掉静态,并且你不能同时对这个类运行两个单元测试。
    6. 使内存管理不灵活。如果您使用面向对象的设计,您可以选择在所有实例之间共享位图,或者为每个实例分配一个新位图。

    【讨论】:

    • 我有一个游戏。父级定义了游戏的一些通用对象(如坐标、移动方法等)。子类是具有定义属性的实际对象,但它们的图片必须从 Game 类加载。我曾经用构造 someObject(int x, int y, Texture2D pic) 给他们图片,但将图片保存到类中似乎更好,所以我不必每次创建类的实例时都将它发送给构造函数。
    • 构造函数听起来是一种更好的方法。不要担心传递引用,因为它只是一个引用.. 每次将它传递给构造函数时它不会复制您的图像,因此如果您构造它们,您将获得在所有实例之间共享内存的好处对相同 Texture2D 的引用。
    【解决方案3】:

    您可以使用静态 Dictionary&lt;Type,Texture2D&gt; 来做到这一点。

    public class Parent
    {
        // Keep a table of types and default values
        protected static Dictionary<Type, Texture2D> pictureOrigin;
    
        static Parent()
        {
            // static ctor. initialize table
            pictureOrigin=new Dictionary<Type, Texture2D>();
        }
    
        internal static void SetDefaultPicture<T>(Texture2D picture)
        {
            // Set default based on type T
            Type type=typeof(T);
            pictureOrigin[type]=picture;
        }
    
        public Parent()
        {
            // Assign default based on this type
            Picture=pictureOrigin[this.GetType()];
        }
        public Texture2D Picture { get; set; }
    }
    
    public class SubClass1 : Parent
    {
    }
    public class SubClass2 : Parent
    {
    }
    

    用作

        static void Main(string[] args)
        {
            Texture2D picture0 = Load("pictureForParent");
            Texture2D picture1=Load("pictureFroSubClass1");
            Texture2D picture2=Load("pictureFroSubClass2");
    
            Parent.SetDefaultPicture<Parent>(picture0);
            Parent.SetDefaultPicture<SubClass1>(picture1);
            Parent.SetDefaultPicture<SubClass2>(picture2);            
        }
    

    这是一个示例的调试。说明SubClass1自动初始化为pictureForSubClass1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-17
      • 2018-07-13
      • 1970-01-01
      • 2011-06-30
      • 2012-01-13
      • 2011-11-20
      • 2011-09-17
      • 2021-01-20
      相关资源
      最近更新 更多