我有几个抽象类,并希望确保"管理器"始终向"托管"类注册,以便它们保留一对多关系的双向知识。也就是说,管理器知道它拥有的所有托管类,并且托管类知道它的管理器是谁(如果它已注册到一个(。此外,我希望托管类能够调用其具体管理器的专业化,而无需进行特殊转换。这可能吗?
我想要这样的东西,但遇到了编译问题:
class Program
{
static void Main(string[] args)
{
ConcreteManager manager = new ConcreteManager();
ConcreteManaged managed = new ConcreteManaged() { Name = "Test" };
manager.Add(managed);
managed.Process();
}
}
public abstract class BaseManager<ManagedType>
where ManagedType : BaseManaged
{
protected Dictionary<string, ManagedType> registered = new Dictionary<string, ManagedType>();
public void Add(ManagedType managed)
{
managed.Manager = this; // Cannot implicitly convert type 'BaseManager<ManagedType>' to 'BaseManager<BaseManaged>' (I've tried casting to no avail)
registered.Add(managed.Name, managed);
}
// Other common management tasks
}
public class ConcreteManager : BaseManager<BaseManaged>
{
//specialization stuff, e.g.
public void Refresh() { Console.WriteLine("Refresh Called"); }
}
public abstract class BaseManaged
{
public string Name { get; set; }
public BaseManager<BaseManaged> Manager { get; set; }
}
public class ConcreteManaged : BaseManaged
{
//specialization stuff, e.g.
public void Process()
{
Manager.Refresh();
}
}
如果我稍微更改一下非Program
类,如下所示,我可以让它编译,但是存在运行时错误(无法将类型为"TestAbstractGenerics.ConcreteManager"的对象转换为类型"TestAbstractGenerics.IBaseManager'1[TestAbstractGenerics.IBaseManaged]"(。
public interface IBaseManager<ManagedType>
where ManagedType : IBaseManaged
{
void Add(ManagedType service);
}
public abstract class BaseManager<ManagedType> : IBaseManager<ManagedType>
where ManagedType : IBaseManaged
{
protected Dictionary<string, ManagedType> registered = new Dictionary<string, ManagedType>();
public void Add(ManagedType managed)
{
managed.Manager = (IBaseManager<IBaseManaged>)this;
registered.Add(managed.Name, managed);
}
// Other common management tasks
}
public class ConcreteManager : BaseManager<BaseManaged>
{
//specialization stuff, e.g.
public void Refresh() { Console.WriteLine("Refresh() called"); }
}
public interface IBaseManaged
{
string Name { get; set; }
IBaseManager<IBaseManaged> Manager { get; set; }
}
public abstract class BaseManaged : IBaseManaged
{
public string Name { get; set; }
public IBaseManager<IBaseManaged> Manager { get; set; }
}
public class ConcreteManaged : BaseManaged
{
//specialization stuff, e.g.
public void Process()
{
((ConcreteManager)Manager).Refresh();
}
}
如果我将IBaseManager<IBaseManaged>
更改为dynamic
我可以从Process()
中删除强制转换,并且一切都按预期工作,但动态不适用于智能感知,我希望能够强制执行类型检查(例如,实现者不会意外地将Manager
设置为string
(。那么这里的最佳实践是什么?是否有一个好的模式可以让我保持一对多的关系?
是的,在上面,我必须添加一些逻辑以确保当BaseManaged.Manager
set
时,它会从其当前Manager
中取消注册,如果有的话。为了简单起见,我在这里避免了这一点。
编辑:这有效,但在调用其非接口方法之前仍然需要强制转换为ConcreteManager
:
class Program
{
static void Main(string[] args)
{
var manager = new ConcreteManager();
var managed = new ConcreteManaged() { Name = "Test"};
manager.Add(managed);
managed.Process();
}
}
public interface IBaseManager<ManagedType>
where ManagedType : IBaseManaged
{
void Add(ManagedType managed);
}
public abstract class BaseManager<ManagedType> : IBaseManager<ManagedType>
where ManagedType : IBaseManaged
{
protected Dictionary<string, ManagedType> registered = new Dictionary<string, ManagedType>();
public void Add(ManagedType managed)
{
managed.Manager = (IBaseManager<IBaseManaged>)this;
registered.Add(managed.Name, managed);
}
// Other common management tasks
}
public class ConcreteManager : BaseManager<IBaseManaged>
{
//specialization stuff, e.g.
public void Refresh() { Console.WriteLine("Refresh() called"); }
}
public interface IBaseManaged
{
string Name { get; set; }
IBaseManager<IBaseManaged> Manager { get; set; }
}
public abstract class BaseManaged : IBaseManaged
{
public string Name { get; set; }
public IBaseManager<IBaseManaged> Manager { get; set; }
}
public class ConcreteManaged : BaseManaged
{
//specialization stuff, e.g.
public void Process()
{
((ConcreteManager)Manager).Refresh();
}
}
确定你想要的那种循环关系不可能实现完美的类型安全且没有强制转换,因为如果你也希望编译器IBaseManaged
也是泛型的(即,IBaseManaged<T> where T : IBaseManager<?>
(,编译器最终会进入无限循环,显然不可能指定你需要的约束来代替?
。
但是,您可以创建第三个接口/类,它可以完全表达这种循环约束,这可能会提供替代解决方案。
interface IManagerAdapter<TManager, TManaged>
where TManager : IBaseManager<TManaged>
where TManaged : IBaseManaged<TManager>
IMO,如果你的ConcreteManaged
类无论如何都要通过类型转换直接了解ConcreteManager
,这些类实际上并没有提供比具体类型遵循的模式更多的东西,抽象有点被破坏了。如果你无论如何都需要特定具体管理器和托管类型之间的这种紧密耦合,我可能会通过在每个类中添加特定类型来代替Managed
来明确它,并取消BaseManaged
类,除了提供Name
之外,它没有多大帮助,这很简单,可以在具体实例中重新实现。
public interface IBaseManaged<T> {
string Name { get; set; }
T Manager { get; set; }
}
public class ConcreteManaged : IBaseManaged<ConcreteManager> {
public string Name { get; set; }
public ConcreteManager Manager { get; set; }
public void Process ()
{
Manager.Refresh ();
}
}
对于基类型中可能比Name
更复杂实现的任何内容,我会选择类似 Mixin 的方法,您可以在单独的类中实现该附加功能,并且只需在接口中提供一个属性来检索 Mixin。例如,如果所有 Manager 类都需要考虑注册所有托管(与Add()
一样(,则显然不希望在每个管理器中复制该功能 - 但您可以通过实现一些 ManagedRegister<T>
类型来简化方法,例如(可以是您喜欢的任何类型(,并为IBaseManager
类型提供一个 Registered
字段以检索实例。
public interface IBaseManager<T> {
ManagedRegister<T> Registered { get; set; }
}
public class ConcreteManager : IBaseManager<ConcreteManaged> {
public ManagedRegister<ConcreteManaged> Registered { get; set; }
public void Refresh () { Console.WriteLine("Refresh() called"); }
}
您仍然可以从此处的Manager
内的寄存器中获得强类型的Managed
实例。
调用代码的变化是,它不是manager.Add(managed)
,而是manager.Registered.Add(managed)
,并且您还需要创建一个要传递给ConcreteManager
的ManagedRegister<ConcreteManaged>
实例。也许有点混乱,我建议将其抽象到工厂中,这样可以防止简单的错误,例如忘记将托管实例添加到管理器。我们可以从上面使用该循环约束以类型安全的方式实现它。(如果可以假设每个托管/管理器都有一个无参数构造函数,则单个实现将使用new()
约束来工作。否则,您将需要一个抽象工厂并为每个具体类型实现(。
interface IManagerFactory<TManager, TManaged>
where TManager : IBaseManager<TManaged>
where TManaged : IBaseManaged<TManager>
{
TManager Manager { get; }
TManaged Create (string name);
}
public abstract class ManagerFactory<TManager, TManaged>
: IManagerFactory<TManager, TManaged>
where TManager : IBaseManager<TManaged>, new()
where TManaged : IBaseManaged<TManager>, new()
{
TManager manager = new TManager ();
public ManagerFactory () {
manager.Registered = new ManagedRegister<TManaged> ();
}
public TManager Manager { get { return manager; } }
public TManaged Create (string name)
{
TManaged result = new TManaged ();
result.Name = name;
manager.Registered.Add (result.Name, result);
result.Manager = manager;
return result;
}
}
public class ConcreteFactory
: ManagedFactory<ConcreteManager, ConcreteManaged> { }
回到主,这里的用法稍微简化了。
ConcreteFactory f = new ConcreteFactory ();
ConcreteManaged managed = f.CreateManaged ("Test");
managed.Process ();
编辑:
这里将所有通用功能抽象为所谓的"基类"。这里的关键区别在于,Base 类通过 Base
属性组成具体类而不是继承,其作用非常类似于通常用于调用 base 成员的 base.
前缀。
public class BaseManager<T> {
public Dictionary<string, T> Registered { get; set; }
}
public interface IBaseManager<T> {
BaseManager<T> Base { get; set; }
}
public class ConcreteManager
: IBaseManager<ConcreteManaged> {
public BaseManager<ConcreteManaged> Base { get; set; }
public void Refresh() { Console.WriteLine("Refresh() called"); }
}
public class BaseManaged<T> {
public string Name { get; set; }
public T Manager { get; set; }
}
public interface IBaseManaged<T> {
BaseManaged<T> Base { get; set; }
}
public class ConcreteManaged
: IBaseManaged<ConcreteManager> {
public BaseManaged<ConcreteManager> Base { get; set; }
internal ConcreteManaged () { }
public void Process () {
Base.Manager.Refresh ();
}
}
interface IManagerFactory<TManager, TManaged>
where TManager : IBaseManager<TManaged>
where TManaged : IBaseManaged<TManager> {
TManager Manager { get; }
TManaged Create (string name);
}
public abstract class BaseManagerFactory<TManager, TManaged>
: IManagerFactory<TManager, TManaged>
where TManager : IBaseManager<TManaged>, new()
where TManaged : IBaseManaged<TManager>, new() {
TManager manager = new TManager();
public BaseManagerFactory() {
manager.Base = new BaseManager<TManaged>();
manager.Base.Registered = new Dictionary<string, TManaged>();
}
public TManager Manager { get { return manager; } }
public TManaged Create (string name) {
TManaged result = new TManaged();
result.Base = new BaseManaged<TManager>();
result.Base.Name = name;
manager.Base.Registered.Add (name, result);
result.Base.Manager = manager;
return result;
}
}