2

背景:我想“扩展”.NET类型以支持与基础对象Lazy<>之间的隐式转换,以便能够自动解包包含的值。我能够很容易地做到这一点:Lazy<T>T

public class ExtendedLazy<T> : Lazy<T>
{
    public ExtendedLazy() : base() {}
    public ExtendedLazy(bool isThreadSafe) : base(isThreadSafe) { }
    public ExtendedLazy(Func<T> valueFactory) : base(valueFactory) { }
    // other constructors

    public static implicit operator T(ExtendedLazy<T> obj)
    {
        return obj.Value;
    }
}

我想通过使协变更进一步,T这样我就可以分配ExtendedLazy<Derived>to的实例ExtendedLazy<Base>。由于类定义中不允许使用方差修饰符,我不得不求助于一个空接口来实现这一点:

public interface IExtendedLazy<out T>
{
}

并将我的类定义更改为

public class ExtendedLazy<T> : Lazy<T>, IExtendedLazy<T>

这工作正常,我能够使用这种协变类型:

ExtendedLazy<DerivedClass> derivedLazy = new ExtendedLazy<DerivedClass>();
IExtendedLazy<BaseClass> baseLazy = derivedLazy;

虽然这编译和工作正常,但它违反了CA1040:避免空接口,它说使用空接口作为合同是一种糟糕的设计和代码味道(我相信大多数人都同意)。我的问题是,鉴于 CLR 无法识别类定义中的变体泛型类型,还有哪些其他方法可以使其与可接受的 OO 实践更加一致?我想我不是唯一面临这个问题的人,所以希望能对此有所了解。

4

1 回答 1

2

你的逻辑不会像你想象的那么好。

ExtendedLazy<DerivedClass> derivedLazy = new ExtendedLazy<DerivedClass>();
IExtendedLazy<BaseClass> baseLazy = derivedLazy;
BaseClass v = baseLazy;

这不会编译,因为不存在从IExtendedLazy<BaseClass>toBaseClass的转换,因为转换运算符仅定义为ExtendedLazy<T>.

这将迫使您在使用界面时执行其他操作。添加T Value { get; }解决了 CA1040 的问题,并让您可以访问基础价值。

顺便说一句,Lazy<T>不提供 an的原因implicit operator T是因为底层Func<T>可能会抛出这会令人困惑,因为抛出的行很可能没有函数(或属性)调用。

于 2015-10-01T21:26:50.530 回答