我有以下接口和委托:
// Base devices, non-generic
public interface IDevice
{
event DeviceVarChange VarChange;
}
public interface IVar
{
// ...
}
public delegate void DeviceVarChange(IVar theVar);我希望能够用更具体的事件参数创建更具体的接口,同时,让它们与上面的基本接口兼容并“兼容”。例如:
// Specific devices, non-generic
public interface ISpecificDevice : IDevice
{
// Problem: I have my "VarChange" event, but it uses IVar!
// I want it to use "ISpecificVar"!
}
public interface ISpecificVar
{
// ...
}所以我选择了一个通用的解决方案。因为我希望我的子接口是“兼容变量的”,并且客户端只“消费”我设备中的变量,所以我尝试使用协方差:
// Base devices, generic
public interface IDevice<out TVar> where TVar : IVar
{
// Compilation error: TVar must be invariant but is covariant in this context
event DeviceVarChange<TVar> VarChange;
}
public interface IVar
{
// ...
}
// Problem: Delegate arguments can be contravariant, but not covariant
public delegate void DeviceVarChange<TVar>(TVar theVar) where TVar : IVar;但是我有一个问题,委托参数不能是协变的。将委托参数设置为逆变量可以解决编译错误,但这样我就不能再像IDevice<IVar>那样使用事件的ISpecificDevice (运行时错误,委托必须是相同类型的)。
public interface IDevice<out TVar> where TVar : IVar
{
// No more compilation error, "DeviceVarChange" is contravariant
event DeviceVarChange<TVar> VarChange;
}
public interface IVar
{
// ...
}
public delegate void DeviceVarChange<in TVar>(TVar theVar) where TVar : IVar;
public interface ISpecificDevice : IDevice<ISpecificVar>
{
// The event is using "ISpecificVar" here, as wanted
}
public interface ISpecificVar : IVar
{
// ...
}
// + a "SpecificDevice" class implementing "ISpecificDevice"
// Try to use a specific instance as a base one
public class Test
{
public void DoTest()
{
var genericDevice = GetSpecificDeviceAsBase();
// Runtime error: delegates must be of the same type
genericDevice.VarChange += OnVarChange;
}
public IDevice<IVar> GetSpecificDeviceAsBase()
{
return new SpecificDevice();
}
public void OnVarChange(IVar theVar)
{
// ...
}
}有没有办法解决这个问题?
或者使用第一种非通用解决方案,并在“特定”事件中强制显式转换是唯一的方法?
我也可以使用一个不变的泛型DeviceVarChange<TVar>委托,非泛型接口,并在每个具有特定接口的子接口中添加一个新事件,但如果我可以避免这个问题,欢迎提出建议。
发布于 2019-12-05 00:07:55
这是我目前的解决办法,作为第一个,希望是暂时的答案。这是我将使用的,同时我希望有人建议我使用一些更好的东西。
public interface IDevice
{
event DeviceVarChange<IVar> VarChange;
}
public interface IVar
{
// ...
}
public delegate void DeviceVarChange<TVar>(TVar theVar) where TVar : IVar;
public interface ISpecificDevice : IDevice
{
// Overload the event inside the interface with the specific class
new event DeviceVarChange<ISpecificVar> VarChange;
}
public interface ISpecificVar : IVar
{
// ...
}
public class SpecificDevice : ISpecificDevice
{
// Dear maintainer,
// Forgive me.
// Regards
private void FireVarChange(ISpecificVar theVar)
{
lock (delegatesForVarChange)
{
foreach (var theDelegate in delegatesForVarChange)
{
theDelegate.Invoke(theVar);
}
}
}
private List<dynamic> delegatesForVarChange = new List<dynamic>();
event DeviceVarChange<IVar> IDevice.VarChange
{
add
{
lock (delegatesForVarChange)
{
delegatesForVarChange.Add(value);
}
}
remove
{
lock (delegatesForVarChange)
{
delegatesForVarChange.Remove(value);
}
}
}
public event DeviceVarChange<ISpecificVar> VarChange
{
add
{
lock (delegatesForVarChange)
{
delegatesForVarChange.Add(value);
}
}
remove
{
lock (delegatesForVarChange)
{
delegatesForVarChange.Remove(value);
}
}
}
}
// Try to use a specific instance as a base one
public class Test
{
public void DoTest()
{
var genericDevice = GetSpecificDeviceAsBase();
// Success
genericDevice.VarChange += OnVarChange;
}
public IDevice GetSpecificDeviceAsBase()
{
return new SpecificDevice();
}
public void OnVarChange(IVar theVar)
{
// ...
}
}https://stackoverflow.com/questions/59177626
复制相似问题