首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >协变泛型接口内符合变体的C#事件

协变泛型接口内符合变体的C#事件
EN

Stack Overflow用户
提问于 2019-12-04 21:53:24
回答 1查看 92关注 0票数 1

我有以下接口和委托:

代码语言:javascript
复制
// Base devices, non-generic
public interface IDevice
{
    event DeviceVarChange VarChange;
}

public interface IVar
{
    // ...
}

public delegate void DeviceVarChange(IVar theVar);

我希望能够用更具体的事件参数创建更具体的接口,同时,让它们与上面的基本接口兼容并“兼容”。例如:

代码语言:javascript
复制
// 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
{
    // ...
}

所以我选择了一个通用的解决方案。因为我希望我的子接口是“兼容变量的”,并且客户端只“消费”我设备中的变量,所以我尝试使用协方差:

代码语言:javascript
复制
// 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 (运行时错误,委托必须是相同类型的)。

代码语言:javascript
复制
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>委托,非泛型接口,并在每个具有特定接口的子接口中添加一个新事件,但如果我可以避免这个问题,欢迎提出建议。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-12-05 00:07:55

这是我目前的解决办法,作为第一个,希望是暂时的答案。这是我将使用的,同时我希望有人建议我使用一些更好的东西。

代码语言:javascript
复制
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)
    {
        // ...
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59177626

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档