C# 委托配置 Action<T> 在派生 class 的一般类型集合上
C# delegate configuration Action<T> on a generically type collection of a derived class
我正在努力向类型为抽象基础的集合提供委托配置操作 class。
派生的 classes 在集合中注册,在 运行 时,选择、初始化正确的基础 class,我希望我能传递一个操作来配置从 CreateInstance
调用传入的公共参数之外的实例。
下面的示例非常接近我想要实现的目标。在我试图将 Action<T>
分配给 FooDefinition<T>
对象的 RegisterDefinition
方法中,车轮从公共汽车上掉下来。我尝试了各种方法,包括使用接口代替和/或与抽象 class 和 Func
结合使用,但未能正确连接。
对于上下文,Bar
class 代表某种处理器,它将接收进入其中的内容,并将其传递给相应的 FooBase
class .派生的 classes 将知道如何处理特定的东西,但可能共享资源(如阻塞队列)。 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}");
}
}
虽然我意识到我可以在创建实例时打开类型,但这会将 Bar
class 与每个派生的 ```FooBase`` 的实现要求相结合 class,解决方案似乎并不干净。
核心问题似乎是关于如何将 Action<T>
转换为 Action<FooBase>
。您必须放弃类型安全并强制转换参数:
ConfigAction = x => configAction?.Invoke((T)x)
此外,我认为 FooDefinition
根本没有任何意义应该是通用的。这样你就不会再得到 type-safe 了,因为你似乎已经从 window 中抛出了 type-safety:没有编译时检查 DerivedFoo
会是 typeof(T)
,在吗?另外,您只使用 FooDefinition<FooBase>
而不是 FooDefinition<AnythingElse>
,因此 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();
}
}
我最终将 MinIoC 用于 DI,然后在派生的 class 构造函数中解决了所需的服务。
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";
}
}
我正在努力向类型为抽象基础的集合提供委托配置操作 class。
派生的 classes 在集合中注册,在 运行 时,选择、初始化正确的基础 class,我希望我能传递一个操作来配置从 CreateInstance
调用传入的公共参数之外的实例。
下面的示例非常接近我想要实现的目标。在我试图将 Action<T>
分配给 FooDefinition<T>
对象的 RegisterDefinition
方法中,车轮从公共汽车上掉下来。我尝试了各种方法,包括使用接口代替和/或与抽象 class 和 Func
结合使用,但未能正确连接。
对于上下文,Bar
class 代表某种处理器,它将接收进入其中的内容,并将其传递给相应的 FooBase
class .派生的 classes 将知道如何处理特定的东西,但可能共享资源(如阻塞队列)。 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}");
}
}
虽然我意识到我可以在创建实例时打开类型,但这会将 Bar
class 与每个派生的 ```FooBase`` 的实现要求相结合 class,解决方案似乎并不干净。
核心问题似乎是关于如何将 Action<T>
转换为 Action<FooBase>
。您必须放弃类型安全并强制转换参数:
ConfigAction = x => configAction?.Invoke((T)x)
此外,我认为 FooDefinition
根本没有任何意义应该是通用的。这样你就不会再得到 type-safe 了,因为你似乎已经从 window 中抛出了 type-safety:没有编译时检查 DerivedFoo
会是 typeof(T)
,在吗?另外,您只使用 FooDefinition<FooBase>
而不是 FooDefinition<AnythingElse>
,因此 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();
}
}
我最终将 MinIoC 用于 DI,然后在派生的 class 构造函数中解决了所需的服务。
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";
}
}