[英]Variant-compliant C# events inside a covariant generic interface
I have the following interfaces and delegate:我有以下接口和委托:
// Base devices, non-generic
public interface IDevice
{
event DeviceVarChange VarChange;
}
public interface IVar
{
// ...
}
public delegate void DeviceVarChange(IVar theVar);
I want to be able to create more specific interfaces with more specific event arguments and, at the same time, have them compatible and "variant-compliant" with the base interfaces above.我希望能够使用更具体的事件参数创建更具体的接口,同时让它们与上述基本接口兼容和“符合变体”。 For example:
例如:
// 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
{
// ...
}
So I went for a generic solution.所以我去找了一个通用的解决方案。 Since I want my subinterfaces to be "variant-compliant" and clients only "consumes" variables from my devices, I tried to use covariance:
由于我希望我的子接口“符合变体”并且客户端只“消耗”我设备中的变量,因此我尝试使用协方差:
// 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;
But I have the issue that delegate arguments cannot be covariant.但我有委托参数不能协变的问题。 Making the delegate arguments contravariant would resolve the compilation error, but then I would no longer be able to use a
ISpecificDevice
like a IDevice<IVar>
for events (runtime error, delegates must be of the same type).使委托参数逆变将解决编译错误,但随后我将无法再使用
ISpecificDevice
类的IDevice<IVar>
事件(运行时错误,委托必须是相同类型)。
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)
{
// ...
}
}
Is there a way out of this issue?有没有办法解决这个问题?
Or is using the first, non-generic solution and force explicit casts in the "specific" events the only way?还是使用第一个非通用解决方案并强制在“特定”事件中进行显式转换是唯一的方法?
I could as well go for a invariant generic DeviceVarChange<TVar>
delegate, non-generic interfaces, and add a new event in every subinterfaces with the specific interface, but if I could dodge this, suggestions are welcome.我也可以选择一个不变的通用
DeviceVarChange<TVar>
委托、非通用接口,并在每个具有特定接口的子接口中添加一个新事件,但如果我能避免这种情况,欢迎提出建议。
Here is my current workaround as a first, hopefully transient answer.这是我目前的第一个解决方法,希望是暂时的答案。 This is what I will use while I hope that someone suggests me something better to work with.
这就是我将使用的,同时我希望有人建议我使用更好的东西。
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)
{
// ...
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.