【问题标题】:C# delegate configuration Action<T> on a generically type collection of a derived class派生类的通用类型集合上的 C# 委托配置 Action<T>
【发布时间】:2020-08-26 00:09:53
【问题描述】:

我正在努力为类型为抽象基类的集合提供委托配置操作。

派生类在集合中注册,在运行时,选择、初始化正确的基类,我希望我可以传递一个动作来配置从传入的公共参数之外的实例CreateInstance 电话。

下面的示例非常接近我想要实现的目标。在我试图将Action&lt;T&gt; 分配给FooDefinition&lt;T&gt; 对象的RegisterDefinition 方法中,车轮从公共汽车上掉了下来。我尝试了各种方法,包括使用接口代替或与抽象类结合使用,以及Func,但未能正确连接。

对于上下文,Bar 类表示某种处理器,它将接收进入它的内容,并将其传递给相应的 FooBase 类。派生类将知道如何处理该特定内容,但可能共享资源(如阻塞队列)。 FooOne可能与FooTwo共享一个阻塞队列,但不是FooThree——因此需要在注册时对其进行配置。

class Program
    {
        static void Main(string[] args)
        {
            var bar = new Bar();

            // Register Foos
            bar.RegisterDefinition<FooOne>("1");
            bar.RegisterDefinition<FooTwo>("2", a => a.fooTwoResource = "McFoo");
            bar.RegisterDefinition<FooThree>("3", a => {
                a.fooThreeResource_1 = "McFoo";
                a.fooThreeResource_2 = "McBar";
            });

            // Foo user selection
            Console.WriteLine("Do you want FooOne(1), or FooTwo(2), or FooTwo(3)");
            var fooSelection = Console.ReadLine();
            
            if (fooSelection == "1" || fooSelection == "2" || fooSelection == "3")
            {
                bar.RunFoo(fooSelection);
            }
            else
            {
                Console.WriteLine("Invalid foo, no bar for you!");
            }

            Console.ReadLine();
        }
    }

    public class Bar
    {
        private List<FooDefinition<FooBase>> fooDefinitions;

        public Bar()
        {
            fooDefinitions = new List<FooDefinition<FooBase>>();
        }

        public void RegisterDefinition<T>(string name) where T : FooBase
        {
            RegisterDefinition<T>(name, null);
        }

        public void RegisterDefinition<T>(string name, Action<T> configAction) where T : FooBase
        {
            if (string.IsNullOrEmpty(name)) throw new ArgumentException("You must name the foo");
            if (fooDefinitions.Any(p => p.Name == name)) throw new InvalidOperationException("Unable to duplicate foo");

            fooDefinitions.Add(
                new FooDefinition<FooBase>()
                {
                    Name = name,
                    DerivedFoo = typeof(T),
                    //ConfigAction = configAction // Cannot implicitly convert type 'System.Action<T>' to 'System.Action<InitializationAction<FooBase>>'.
                    //ConfigAction = (Action<FooBase>)configAction // Compiles, but results in an InvalidCastException when run. 'Unable to cast object of type 'System.Action`1[InitializationAction.FooTwo]' to type 'System.Action`1[InitializationAction.FooBase]'.'
                });
        }

        public void RunFoo(string fooName)
        {
            var fooDefinition = fooDefinitions.FirstOrDefault(p => p.Name == fooName);
            if (fooDefinition == null) return;

            // Create a new instance of the class, and pass in a common resource (in this example, a string).
            var fooInstance = Activator.CreateInstance(fooDefinition.DerivedFoo, "foo") as FooBase;

            // Run an action that would set any additional properties as necessary
            fooDefinition.ConfigAction?.Invoke(fooInstance);

            fooInstance.Bared();
        }
    }

    public class FooDefinition<T> where T : FooBase
    {
        public string Name { get; set; }
        public Type DerivedFoo { get; set; }
        public Action<T> ConfigAction { get; set; }
    }

    // I tried an interface, it did not work.
    //public interface IFoo
    //{
    //    string fooString { get; }
    //    void Go();
    //}

    //public abstract class FooBase : IFoo
    public abstract class FooBase
    {
        public string fooString { get; private set; }

        public FooBase(string fooString)
        {
            this.fooString = fooString;
        }

        public abstract void Bared();
    }

    public class FooOne : FooBase
    {
        public FooOne(string fooString) : base(fooString) { }

        public override void Bared()
        {
            Console.WriteLine($"DerivedFoo: {nameof(FooOne)}: {fooString}");
        }
    }

    public class FooTwo : FooBase
    {
        public string fooTwoResource { get; set; }

        public FooTwo(string fooString) : base(fooString) { }

        public override void Bared()
        {
            Console.WriteLine($"DerivedFoo: {nameof(FooTwo)}: {fooString} {fooTwoResource}");
        }
    }

    public class FooThree : FooBase
    {
        public string fooThreeResource_1 { get; set; }
        public string fooThreeResource_2 { get; set; }

        public FooThree(string fooString) : base(fooString){ }

        public override void Bared()
        {
            Console.WriteLine($"DerivedFoo: {nameof(FooThree)}: {fooString} {fooThreeResource_1} {fooThreeResource_2}");
        }
    }

虽然我意识到我可以在创建实例时打开 Type,但这会将 Bar 类与每个派生的 ``FooBase` 类的实现要求结合起来,并且看起来不像解决方案。

【问题讨论】:

    标签: c# generics delegates polymorphism


    【解决方案1】:

    核心问题似乎是关于如何将Action&lt;T&gt; 转换为Action&lt;FooBase&gt;。您必须放弃类型安全并强制转换参数:

    ConfigAction = x => configAction?.Invoke((T)x)
    

    此外,我认为FooDefinition 中的任何一点都不应该是通用的。这样你就不再获得类型安全了,因为你似乎已经把类型安全抛到了窗外:没有编译时检查DerivedFoo 将是typeof(T),是吗?另外,您只使用FooDefinition&lt;FooBase&gt; 而不是FooDefinition&lt;AnythingElse&gt;,所以FooDefinition 可能只是:

    public class FooDefinition
    {
        public string Name { get; set; }
        public Type DerivedFoo { get; set; }
        public Action<FooBase> ConfigAction { get; set; }
    }
    

    为了完整起见,这里是Bar

    public class Bar
    {
        // note the change in type
        private List<FooDefinition> fooDefinitions;
    
        public Bar()
        {
            fooDefinitions = new List<FooDefinition>();
        }
    
        public void RegisterDefinition<T>(string name) where T : FooBase
        {
            RegisterDefinition<T>(name, null);
        }
    
        public void RegisterDefinition<T>(string name, Action<T> configAction) where T : FooBase
        {
            if (string.IsNullOrEmpty(name)) throw new ArgumentException("You must name the foo");
            if (fooDefinitions.Any(p => p.Name == name)) throw new InvalidOperationException("Unable to duplicate foo");
    
            fooDefinitions.Add(
                new FooDefinition()
                {
                    Name = name,
                    DerivedFoo = typeof(T),
                    ConfigAction = x => configAction?.Invoke((T)x)
                });
        }
    
        public void RunFoo(string fooName)
        {
            var fooDefinition = fooDefinitions.FirstOrDefault(p => p.Name == fooName);
            if (fooDefinition == null) return;
    
            // Create a new instance of the class, and pass in a common resource (in this example, a string).
            var fooInstance = Activator.CreateInstance(fooDefinition.DerivedFoo, "foo") as FooBase;
    
            // Run an action that would set any additional properties as necessary
            fooDefinition.ConfigAction?.Invoke(fooInstance);
    
            fooInstance.Bared();
        }
    }
    

    【讨论】:

      【解决方案2】:

      我最终将MinIoC 用于 DI,然后在派生类构造函数中解析了所需的服务。

      static void Main(string[] args)
              {
                  var bar = new Bar();
      
                  // Register Services
                  bar.Container.Register<FooService>();
      
                  // Register Foos
                  bar.RegisterFoo<FooOne>("1");
                  bar.RegisterFoo<FooTwo>("2");
      
                  // Foo user selection
                  Console.WriteLine("Do you want FooOne(1), or FooTwo(2)");
                  var fooSelection = Console.ReadLine();
                  
                  if (fooSelection == "1" || fooSelection == "2")
                  {
                      bar.RunFoo(fooSelection);
                  }
                  else
                  {
                      Console.WriteLine("Invalid foo, no bar for you!");
                  }
      
                  Console.ReadLine();
              }
          }
      
          public class Bar
          {
              private Dictionary<string, Type> registeredFoos;
              public Container Container { get; private set; } 
      
              public Bar()
              {
                  Container = new Container();
                  registeredFoos = new Dictionary<string, Type>();
              }
      
              public void RegisterFoo<T>(string name) where T : FooBase
              {
                  registeredFoos.Add(name, typeof(T));
              }
      
              public void RunFoo(string fooName)
              {
                  if (registeredFoos.TryGetValue(fooName, out var fooType))
                  {
                      var fooInstance = Activator.CreateInstance(fooType, new object[] { "foo", Container }) as FooBase;
                      fooInstance?.Bared();
                  }
              }
          }
      
          public abstract class FooBase
          {
              protected Container _container;
      
              public string fooString { get; private set; }
      
              public FooBase(string fooString, Container container)
              {
                  this.fooString = fooString;
                  _container = container;
              }
      
              public abstract void Bared();
          }
      
          public class FooOne : FooBase
          {
              public FooOne(string fooString, Container serviceContainer) : base(fooString, serviceContainer) { }
      
              public override void Bared()
              {
                  Console.WriteLine($"DerivedFoo: {nameof(FooOne)}: {fooString}");
              }
          }
      
          public class FooTwo : FooBase
          {
              private readonly FooService fooService;
      
              public FooTwo(string fooString, Container container) : base(fooString, container) 
              {
                  fooService = container.Resolve<FooService>();
              }
      
              public override void Bared()
              {
                  var something = fooService.GetSomething();
      
                  Console.WriteLine($"DerivedFoo: {nameof(FooTwo)}: {fooString} {something}");
              }
          }
      
          public class FooService
          {
              public string GetSomething()
              {
                  return "something";
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-07-02
        • 1970-01-01
        • 1970-01-01
        • 2015-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多