【问题标题】:Singletons, Generic & Inheritance - have all child classes inherit the singleton functionalitySingletons, Generic & Inheritance - 让所有子类继承单例功能
【发布时间】:2013-11-29 10:53:51
【问题描述】:

我有两个问题。一个是我试图拥有一个抽象的单例类,它具有单例功能+一些额外的功能,但是这个类也是一个通用的(与它是单例无关)。我希望它的任何子类在不重写代码的情况下继承单例功能,以及依赖于泛型类型的其余功能。

第二个问题,抽象类有一个非默认构造函数,它应该接收一个字符串值。这个字符串可以由子类的类(继承抽象类的类)来确定。

我的处理方法如下:

  • 向返回字符串参数的抽象类添加一个抽象静态方法,以便每个派生类都实现它,因此该类的任何非抽象实例都可以正常工作。不可能,因为静态方法不能是抽象的。
  • 使用返回字符串的静态方法创建一个接口,并确保泛型类型继承自该接口。然后在实例化单例时,会调用T.GetTheString()作为参数传递给构造函数。不可能,因为接口可能没有静态方法。

有什么想法吗?

这里有一些解释我的问题的简化代码:

public abstract class NamedObject<T> {
    private static NamedObject<T> _instance;
    public static NamedObject<T> Instance {
        get {
            if (_instance == null) {
                _instance = new NamedObject<T>("Need to determine this string from the non-abstract child class of NamedObject");
            }
            return _instance;
        }
    }

    private string objectName;

    public NamedObject(string objectName) {
        this.objectName = objectName;
    }

    public string GetFullName() {
        return objectName + "(" + GetClassName() + ")";
    }

    protected virtual string GetClassName() {
        return typeof(T).ToString();
    }
}

注意我需要GetClassName()的泛型T,并注意要创建单例的实例,我需要调用继承这个类的子类的静态方法(这似乎是不可能的)

编辑:更新问题以更好地反映我的问题。


编辑:感谢大家的帮助。第二个问题的解决方案是使用默认构造函数,并从子类的非静态方法(基类中的抽象)中获取应该是构造函数中的参数的字符串。

此外,我对单例继承的整个想法是错误的,感谢 Rob Lyndon 的回答,我设法解决了这个问题。

这是最终代码(我知道这没有意义,这只是一个简化版...):

public abstract class NamedObject<ME, T> where ME : NamedObject<ME, T>, new()
{
    private static NamedObject<ME, T> _instance;
    public static NamedObject<ME, T> Instance {
        get {
            if (_instance == null) {
                _instance = new ME();
            }
            return _instance;
        }
    }

    private string objectName = "test";

    public NamedObject() {
        this.objectName = GetObjectName();
    }

    public string GetFullName() {
        return objectName + "(" + GetClassName() + ")";
    }

    protected virtual string GetClassName() {
        return typeof(T).ToString();
    }

    protected abstract string GetObjectName();
}

还有子类:

public sealed class MyNamedObject : NamedObject<MyNamedObject, MyClass>
{
    public MyNamedObject() : base() {
    }

    protected override string GetObjectName () {
        return "MyName";
    }
}

所以第一次调用MyNamedObject.Instance时,它会NamedObject()构造函数,MyClassT,而objectName参数将是"MyName"子类定义的MyNamedObject .

正是我想要的。感谢大家的帮助,这很有帮助。

【问题讨论】:

  • 你想在这里做什么?抽象的通用单例吧?您不能创建新的 NamedObject,它是抽象的。抽象单例是什么意思?
  • 对...我错了另一件事:)。我的目标是有一个父类,所有子类都是单例,而不必为每个子类重新编写单例代码。
  • @Mike 我知道单身人士的利弊。它们有它们的用途。就我而言,我使用它们来加载资源,这是可以接受的用途。 c2.com/cgi/wiki?SingletonsAreGood

标签: c# generics singleton


【解决方案1】:

不要做单例。永远。

无论如何,这是一种接近您的用例的方法。

// base class
public abstract class NamedObject<ME, T> where ME : NamedObject<ME, T>, new()
{
    private static NamedObject<ME, T> _instance;
    public static NamedObject<ME, T> Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new ME { objectName = GetLabel(typeof(T)) };
            }
            return _instance;
        }
    }

    private string objectName;

    public string GetFullName()
    {
        return objectName + "(" + GetClassName() + ")";
    }

    protected abstract string GetLabel(Type type);
    protected abstract string GetClassName();
}

【讨论】:

  • 好的,这对我有用,但有一些小的变化:我需要原来的 T,它被替换为脚本中的子类。相反,我使用了NamedObject&lt;ME, T&gt; where ME : NamedObject&lt;ME, T&gt;, new()。唯一用到T的地方是typeof,其余都是ME。其次,我真的不需要typeof 值,我需要一个由类型确定的字符串,而不是类型本身。我最终找到的解决方案是使用一个空的构造函数,并在构造函数本身中调用了一个抽象方法。请至少更新第一部分的答案,我可以接受。
  • 完成。希望你一切顺利。
【解决方案2】:

这应该可以满足您的要求(Name 属性也可以是一个抽象方法,如果我们使用单例模式,作为一个属性似乎更好)。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(MyNamedObject.Instance.Name);
    }
}

public abstract class NamedObject<T> where T : NamedObject<T>, new()
{
    private static T _instance;

    public abstract string Name { get; }

    public static T Instance
    {
        get { return _instance ?? (_instance = new T()); }
    }
}

public class MyNamedObject : NamedObject<MyNamedObject>
{
    public override string Name
    {
        get { return "My Named Object Name"; }
    }
}

【讨论】:

    【解决方案3】:

    我建议你引入一个属性来装饰每种类型,并在需要时提取它。您将无法在编译时验证类型 具有 正确的类型,但对此您无能为力。对您需要的所有类型进行单元测试:)

    [AttributeUsage(AttributeTargets.Class)]
    public class NameAttribute : Attribute
    {
        public NameAttribute(string name)
        {
            this.Name = name;
        }
    
        public string Name { get; set; }
    }
    
    [Name("Foo")]
    public class SomeClass
    {
    }
    

    然后在你的单例类中,使用静态构造函数而不是线程不安全属性。

    private static readonly NamedObject<T> instance;
    
    static NamedObject()
    {
        // TODO: Validation :)
        var attributes = typeof(T).GetCustomAttributes(typeof(NameAttribute), false);
        var firstAttribute = ((NameAttribute[]) attributes)[0];
        string name = firstAttribute.Name;
        instance = new NamedObject<T>(name);
    }
    
    public static NamedObject<T> Instance { get { return instance; } }
    

    或者改用Lazy&lt;&gt;。有关这方面的更多信息,请参阅my article on singletons

    编辑:澄清一下-您不应该将NamedObject 作为一个抽象类开始。单例和继承不能混用NamedObject&lt;T&gt; 应该是一个密封类,它具有任何 T 的单个实例。它并不强制只创建一个 T 实例。

    【讨论】:

    • 我不知道静态构造函数,但它似乎完全符合我需要做的事情(没有装饰,我不太明白......我更像是一个 C++大部分时间的人)。我会检查一下并告诉你。谢谢。
    • @tbkn23:静态构造函数并不能真正解决从哪里获取名称的问题——这是应用attributes思想的问题,我建议你这样做在这里阅读:msdn.microsoft.com/en-us/library/z0w1kczw(v=vs.110).aspx 静态构造函数只是解决线程安全问题。
    • 嗯...实际上我认为静态构造函数可能无济于事,因为子类不继承它...有什么办法可以让基单例类和每个继承的类自动获取无需重写代码的单例功能?我会阅读你的链接,谢谢。
    • @tbkn23:查看我的编辑。基本上,不要尝试将单例与继承本身一起使用。它根本行不通。如果您还需要T 的实例,那就另当别论了——基本上您需要更详细地指定您的要求。如果可能的话,我也会尽量避免使用单例,但这是一个稍微不同的问题。
    • 我明白了。好吧,我希望有一个更简单的解决方案,但主要问题是静态不是继承的......谢谢。
    猜你喜欢
    • 1970-01-01
    • 2018-12-02
    • 1970-01-01
    • 2013-10-21
    • 1970-01-01
    • 2013-04-17
    • 2016-05-08
    • 1970-01-01
    • 2010-11-15
    相关资源
    最近更新 更多