如何避免多个 类 中静态方法的代码重复

How can I avoid code duplication of a static method in multiple classes

我有多个包含重复代码的 classes,尤其是成员和最重要的静态方法,它将创建 class 的新实例并 returning 这个实例:要么先前创建的实例在字典中注册或通过调用构造函数创建新实例。

接口不是选项,因为我有静态方法。我试图通过引入实现此静态方法的基础 class 来解决问题,但我找不到正确创建和 return 特定子 class 的方法。

下面是当前情况的代码示例,其中 class A 和 class B 显示重复代码。

public class A
{
    private static readonly IDictionary<string, A> Registry = new Dictionary<string, A>();
    public string Name { get; set; }

    public A(string name)
    {
        this.Name = name;
    }

    public static A GetA(string instanceName)
    {
        lock (Registry)
        {
            if (!Registry.TryGetValue(instanceName, out var newInstance))
            {
                newInstance = new A(instanceName);
            }
            return newInstance;
        }
    }
}

然后在 class B 中再次有一个成员名称和 GetX() 方法。

public class B
{
    private static readonly IDictionary<string, B> Registry = new Dictionary<string, B>();
    public string Name { get; set; }

    public B(string name)
    {
        this.Name = name;
    }

    public static B GetB(string instanceName)
    {
        lock (Registry)
        {
            if (!Registry.TryGetValue(instanceName, out var newInstance))
            {
                newInstance = new B(instanceName);
            }
            return newInstance;
        }
    }
}

是否可以通过引入基数 class 或任何其他方式来避免这种代码重复?

您是否在寻找通用基础 class?

public abstract class BaseRegistryGetter<T>
{
    private static readonly IDictionary<string, T> Registry = new Dictionary<string, T>();
    public string Name { get; set; }

    public BaseRegistryGetter(string name)
    {
        this.Name = name;
    }

    public static T GetValue (string instanceName, Func<string, T> creator) {
        lock (Registry)
        {
            if (!Registry.TryGetValue(instanceName, out var newInstance))
            {
                newInstance = creator(instanceName);
            }
            return newInstance;
        }
    }
}

然后像这样使用它:

public class A : BaseRegistryGetter<A>
{
    public A(string name) : base(name)
    {
    }

    public static A GetA(string instanceName)
    {
        return BaseRegistryGetter<A>.GetValue(instanceName, s => new A(s));
    }
}

可以找到确保 A 有字符串构造函数的尴尬方法的来源 here

我认为这应该可行。您可以调整它以满足您的需要。另外,您的代码中有一个错误:您在创建新实例时忘记添加到 Registry

class Program
{
    static void Main(string[] args)
    {
        A a1 = A.GetInstance("a");
        A a2 = A.GetInstance("aa");
        A a3 = A.GetInstance("a");

        B b1 = B.GetInstance("a");
        B b2 = B.GetInstance("aa");
        B b3 = B.GetInstance("a");

        Console.WriteLine(a1 == a2); //false
        Console.WriteLine(a1 == a3); //true

        Console.WriteLine(b1 == b2); //false
        Console.WriteLine(b1 == b3); //true

        Console.ReadKey();
    }
}

public class A : Generic<A>
{
    public A(string name)
        : base(name)
    {
    }
}

public class B : Generic<B>
{
    public B(string name)
        : base(name)
    {
    }
}

public abstract class Generic<T> where T : Generic<T>
{
    private static readonly IDictionary<string, T> Registry = new Dictionary<string, T>();
    public string Name { get; set; }

    public Generic(string name)
    {
        this.Name = name;
    }

    public static T GetInstance(string instanceName)
    {
        lock (Registry)
        {
            if (!Registry.TryGetValue(instanceName, out var newInstance))
            {
                newInstance = (T)Activator.CreateInstance(typeof(T), instanceName);
                Registry.Add(instanceName, newInstance);
            }
            return newInstance;
        }
    }
}

这可能更干净一些:

public class B: RegistryInstance<B>
{
    public string Name { get; set; }

    public B(string name)
    {
        this.Name = name;
    }
}

public class A : RegistryInstance<A>
{
    public string Name { get; set; }

    public A(string name)
    {
        this.Name = name;
    }
}

public abstract class RegistryInstance<T> where T:class
{
    protected static readonly IDictionary<string, T> Registry = new Dictionary<string, T>();

    public static T GetInstance(string instanceName)
    {
        lock (Registry)
        {
            if (!Registry.TryGetValue(instanceName, out var newInstance))
            {
                newInstance = (T)Activator.CreateInstance(typeof(T), new object[] { instanceName });
                Registry.Add(instanceName, newInstance);
            }
            return newInstance;
        }
    }
}

所有其他答案都试图用泛型来解决这个问题,但您可能不想这样做。首先,这可能是一个不必要的限制,最终可能会导致差异问题。第二,它只解决了一级继承,如果有更多,你又被同样的问题卡住了:

 class Base<T> { ... }
 class A: Base<A> { ... }
 class B: A { //How does the generic base class help? }

有不使用泛型的通用解决方案,只需要一点代码重复。其中之一可能是:

public class Base
{
    static readonly IDictionary<string, Base> Registry = 
        new Dictionary<string, Base>();

    protected static Base GetBase(string instanceName,
                                  Func<Base> creator)
    {
        lock (Registry)
        {
            if (!Registry.TryGetValue(instanceName, out var newInstance))
            {
                newInstance = creator();
            }   

            return newInstance;
        }
    }

    //...
}

现在您的派生类型可以实现强类型委托方法:

public class A: Base
{
    public A(string instanceName)
        :base(instanceName)
    {
    }
    public static A GetA(string instanceName)
        => GetBase(instanceName, () => new A(instanceName)) as A;
}

public class B: Base
{
    public B(string instanceName)
        :base(instanceName)
    {
    }
    public static B GetB(string instanceName)
        => GetBase(instanceName, () => new B(instanceName)) as B;
}