【问题标题】:How can I avoid code duplication of a static method in multiple classes如何避免多个类中的静态方法的代码重复
【发布时间】:2019-01-03 10:16:20
【问题描述】:

我有多个包含重复代码的类,尤其是成员和最重要的静态方法,它将创建该类的新实例并返回该实例:以前创建的实例在字典中注册或通过调用构造函数创建新实例.

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

下面是当前情况的代码示例,A 类和 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;
        }
    }
}

然后在 B 类中又有一个成员 Name 和 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;
        }
    }
}

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

【问题讨论】:

  • Mabe 制作基本方法,接受泛型类型,然后从中创建实例。见stackoverflow.com/questions/731452/…
  • 但是如果字典是静态的,那么您将在AB 之间共享同一个字典,这可能不是您想要的。
  • 构造函数public 的可见性是有意的吗?
  • 为什么要根据instanceName来限制实例数?是否允许 A 的实例和 B 的实例具有相同的 instanceName
  • Name 可以是基类/接口的一部分。至于GetX,你不能避免声明它,除非你能把它变成通用的,那么 base class 就是它通常应该在的地方。您仍然可以声明类型特定的静态方法来调用该通用方法,以方便这些类型的用户。

标签: c# inheritance code-duplication static-polymorphism


【解决方案1】:

这可能会更干净一点:

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;
        }
    }
}

【讨论】:

  • 您在创建新实例时忘记添加到Registry。这样一来,它就永远不会进入字典。
  • @AndersonPimentel,我一发布就注意到了。感谢您的关注。顺便说一句,答案很好。
  • 感谢@JohnBabb 提供的有用方法。
【解决方案2】:

您在寻找通用基类吗?

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));
    }
}

可在here 找到确保 A 存在字符串构造函数的笨拙方法的来源。

【讨论】:

    【解决方案3】:

    我认为这应该可行。您可以对其进行调整以满足您的需求。此外,您的代码中存在一个错误:您在创建新实例时忘记添加到 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;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

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

       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;
      }
      

      【讨论】:

      • 您是否忘记在您的第一个代码示例中添加到RegistryRegistry 是否可以改为 ConcurrentDictionary(可能值是 Lazy)?
      • @sinatr 对不起,我真的需要去睡觉了。这或多或少是我的想法。是的,另一种选择是使 Register 通用并调用 Activator.CreateInstance 但这依赖于反射......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多