【发布时间】:2020-08-26 00:09:53
【问题描述】:
我正在努力为类型为抽象基类的集合提供委托配置操作。
派生类在集合中注册,在运行时,选择、初始化正确的基类,我希望我可以传递一个动作来配置从传入的公共参数之外的实例CreateInstance 电话。
下面的示例非常接近我想要实现的目标。在我试图将Action<T> 分配给FooDefinition<T> 对象的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