c#泛型协变和逆变冲突
c# generics covariance and contravariance conflict
我使用接口、继承和泛型用 c# 编写了以下代码:
public interface IBasic
{
}
public class Basic : IBasic
{
}
public class AnotherBasic : Basic
{
}
public interface IWorker<in TBasic>
{
void Run(TBasic basic);
}
public class Worker : IWorker<Basic>
{
public void Run(Basic basic)
{
throw new System.NotImplementedException();
}
}
public class AnotherWorker : IWorker<AnotherBasic>
{
public void Run(AnotherBasic basic)
{
throw new System.NotImplementedException();
}
}
public void Test()
{
List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
{
new Worker(),
new AnotherWorker()
};
}
这段代码的问题是工人和另一个工人 classes 不适合 IWorker<IBasic>
的泛型列表,他们是工人和基本 class。问题是 IWorker<in TBasic>
是逆变的,因为 运行 方法签名,但是我需要它是协变的,以便填充 List<IWorker<IBasic>>
。 运行 方法必须有 TBasic 参数,我需要这个工作人员列表是为了责任链设计模式。我是否遗漏了什么,或者我是否找到了使协变和逆变不互斥的理由?
你可以这样初始化它:
public void Test()
{
List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
{
new Worker<IBasic>(),
new AnotherWorker<IBasic>()
};
workers[0].Run(new Basic());
}
你的工人声明说 "this is a list of workers, and each one can run any implementation of IBasic",这是不正确的。
您可以尝试将 worker 可以处理的命令类型的责任转移到 worker 本身(事实上,这就是 chain of responsibility pattern 建议的)。
public interface IWorker
{
bool DidRun<TBasic>(TBasic basic);
}
public class WorkerChain
{
private readonly List<IWorker> workers = new List<IWorker>
{
new Worker(),
new AnotherWorker()
};
public bool DidRun<T>(T basic)
{
return workers.Any(worker => worker.DidRun(basic));
}
}
public class Worker : IWorker
{
public bool DidRun<T>(T basic)
{
if (!(basic is Basic))
{
return false;
}
Console.WriteLine($"running {basic}");
return true;
}
}
public class Test
{
public void CanRunWorkBasic()
{
var didRun = new WorkerChain().DidRun(new Basic());
Debug.Assert(didRun);
}
}
如果你想将你的工人插入到列表中,你将需要一个非通用接口 IWorker
并且 IWorker<TBasic>
必须实现该接口。然后在 List
中使用 IWorker
而不是 IWorker<TBasic>
。现在将您的工人添加到 List
不再有问题。
这样我们解决了一个问题,但不幸的是我们还创建了另一个问题,因为我们必须执行两次Run
方法。一次用于非通用接口,第二次用于通用接口。
您可以使用抽象 Worker
class 来解决这个问题,默认情况下,当调用非泛型 Run
时,会进行必要的检查,转换参数并传递它通用 Run
方法。然后你的工人可以从 Worker
派生,每个人都可以有自己的角色。
在下面的示例中,我试图展示在我看来,这段代码应该是什么样子。实现了非泛型 Run
方法 explicitly and to be safe I also used generic type constraints。 Run
方法只检查类型并将其进一步传递。
public interface IBasic
{
}
public class Basic : IBasic
{
}
public class AnotherBasic : Basic
{
}
public interface IWorker
{
void Run(IBasic basic);
}
public interface IWorker<in TBasic> : IWorker where TBasic : IBasic
{
void Run(TBasic basic);
}
public abstract class Worker<TBasic> : IWorker<TBasic> where TBasic : IBasic
{
void IWorker.Run(IBasic basic)
{
if (basic is TBasic)
Run((TBasic)basic);
}
public abstract void Run(TBasic basic);
}
public class FirstWorker : Worker<Basic>
{
public override void Run(Basic basic)
{
// ...
}
}
public class SecondWorker : Worker<AnotherBasic>
{
public override void Run(AnotherBasic basic)
{
// ...
}
}
public void Test()
{
List<IWorker> workers = new List<IWorker>
{
new FirstWorker(),
new SecondWorker()
};
}
所以经过2天的学习和调查,我回答了我自己的问题。这是代码:
public 界面 IBasic
{
}
public class Basic : IBasic
{
}
public class AnotherBasic : Basic
{
}
public interface IWorker<in TBasic>
{
void Run(TBasic basic);
}
public class SimpleWorker : IWorker<IBasic>
{
public void Run(IBasic basic)
{
throw new System.NotImplementedException();
}
}
public class Worker : IWorker<Basic>
{
public void Run(Basic basic)
{
throw new System.NotImplementedException();
}
}
public class AnotherWorker : IWorker<AnotherBasic>
{
public void Run(AnotherBasic basic)
{
throw new System.NotImplementedException();
}
}
public class Final
{
public void Test()
{
List<IWorker<AnotherBasic>> workers = new List<IWorker<AnotherBasic>>
{
new SimpleWorker(),
new Worker(),
new AnotherWorker()
};
}
}
CONTRAVARIANT中的TBasic,表示声明要尽可能具体,如代码所示:AnotherBasic
然后派生较少的类型,即父类,被接受,代码编译。
我使用接口、继承和泛型用 c# 编写了以下代码:
public interface IBasic
{
}
public class Basic : IBasic
{
}
public class AnotherBasic : Basic
{
}
public interface IWorker<in TBasic>
{
void Run(TBasic basic);
}
public class Worker : IWorker<Basic>
{
public void Run(Basic basic)
{
throw new System.NotImplementedException();
}
}
public class AnotherWorker : IWorker<AnotherBasic>
{
public void Run(AnotherBasic basic)
{
throw new System.NotImplementedException();
}
}
public void Test()
{
List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
{
new Worker(),
new AnotherWorker()
};
}
这段代码的问题是工人和另一个工人 classes 不适合 IWorker<IBasic>
的泛型列表,他们是工人和基本 class。问题是 IWorker<in TBasic>
是逆变的,因为 运行 方法签名,但是我需要它是协变的,以便填充 List<IWorker<IBasic>>
。 运行 方法必须有 TBasic 参数,我需要这个工作人员列表是为了责任链设计模式。我是否遗漏了什么,或者我是否找到了使协变和逆变不互斥的理由?
你可以这样初始化它:
public void Test()
{
List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
{
new Worker<IBasic>(),
new AnotherWorker<IBasic>()
};
workers[0].Run(new Basic());
}
你的工人声明说 "this is a list of workers, and each one can run any implementation of IBasic",这是不正确的。
您可以尝试将 worker 可以处理的命令类型的责任转移到 worker 本身(事实上,这就是 chain of responsibility pattern 建议的)。
public interface IWorker
{
bool DidRun<TBasic>(TBasic basic);
}
public class WorkerChain
{
private readonly List<IWorker> workers = new List<IWorker>
{
new Worker(),
new AnotherWorker()
};
public bool DidRun<T>(T basic)
{
return workers.Any(worker => worker.DidRun(basic));
}
}
public class Worker : IWorker
{
public bool DidRun<T>(T basic)
{
if (!(basic is Basic))
{
return false;
}
Console.WriteLine($"running {basic}");
return true;
}
}
public class Test
{
public void CanRunWorkBasic()
{
var didRun = new WorkerChain().DidRun(new Basic());
Debug.Assert(didRun);
}
}
如果你想将你的工人插入到列表中,你将需要一个非通用接口 IWorker
并且 IWorker<TBasic>
必须实现该接口。然后在 List
中使用 IWorker
而不是 IWorker<TBasic>
。现在将您的工人添加到 List
不再有问题。
这样我们解决了一个问题,但不幸的是我们还创建了另一个问题,因为我们必须执行两次Run
方法。一次用于非通用接口,第二次用于通用接口。
您可以使用抽象 Worker
class 来解决这个问题,默认情况下,当调用非泛型 Run
时,会进行必要的检查,转换参数并传递它通用 Run
方法。然后你的工人可以从 Worker
派生,每个人都可以有自己的角色。
在下面的示例中,我试图展示在我看来,这段代码应该是什么样子。实现了非泛型 Run
方法 explicitly and to be safe I also used generic type constraints。 Run
方法只检查类型并将其进一步传递。
public interface IBasic
{
}
public class Basic : IBasic
{
}
public class AnotherBasic : Basic
{
}
public interface IWorker
{
void Run(IBasic basic);
}
public interface IWorker<in TBasic> : IWorker where TBasic : IBasic
{
void Run(TBasic basic);
}
public abstract class Worker<TBasic> : IWorker<TBasic> where TBasic : IBasic
{
void IWorker.Run(IBasic basic)
{
if (basic is TBasic)
Run((TBasic)basic);
}
public abstract void Run(TBasic basic);
}
public class FirstWorker : Worker<Basic>
{
public override void Run(Basic basic)
{
// ...
}
}
public class SecondWorker : Worker<AnotherBasic>
{
public override void Run(AnotherBasic basic)
{
// ...
}
}
public void Test()
{
List<IWorker> workers = new List<IWorker>
{
new FirstWorker(),
new SecondWorker()
};
}
所以经过2天的学习和调查,我回答了我自己的问题。这是代码:
public 界面 IBasic {
}
public class Basic : IBasic
{
}
public class AnotherBasic : Basic
{
}
public interface IWorker<in TBasic>
{
void Run(TBasic basic);
}
public class SimpleWorker : IWorker<IBasic>
{
public void Run(IBasic basic)
{
throw new System.NotImplementedException();
}
}
public class Worker : IWorker<Basic>
{
public void Run(Basic basic)
{
throw new System.NotImplementedException();
}
}
public class AnotherWorker : IWorker<AnotherBasic>
{
public void Run(AnotherBasic basic)
{
throw new System.NotImplementedException();
}
}
public class Final
{
public void Test()
{
List<IWorker<AnotherBasic>> workers = new List<IWorker<AnotherBasic>>
{
new SimpleWorker(),
new Worker(),
new AnotherWorker()
};
}
}
CONTRAVARIANT中的TBasic,表示声明要尽可能具体,如代码所示:AnotherBasic 然后派生较少的类型,即父类,被接受,代码编译。