为什么这种隐式演员是不可能的?

时间:2018-05-07 01:46:50

标签: c# types least-common-ancestor

鉴于下面的类和接口,我想知道为什么隐式转换:

ISomeModelAbstract<IBasicModel> x = new ConcreteClass();

不可能。我试过了

public interface ISomeModelAbstract<out T> where T: IBasicModel

但是我不能使用GetByIdGetAll方法。我感谢任何帮助或提示。谢谢。

public interface IBasicModel {
    string _id { get; set; }
}

public class SomeModel: IBasicModel {
    public string _id { get; set; }

    /* some other properties! */
}

public interface ISomeModelAbstract<T> where T: IBasicModel
{
    bool Save(T model);
    T GetById(string id);
    IEnumerable<T> GetAll();
    bool Update(string id, T model);
    bool Delete(string id);
}

public abstract class SomeModelAbstract<T> : ISomeModelAbstract<T> where T : IBasicModel
{
    public bool Save(T model)
    {
        throw new System.NotImplementedException();
    }

    public T GetById(string id)
    {
        throw new System.NotImplementedException();
    }

    public IEnumerable<T> GetAll()
    {
        throw new System.NotImplementedException();
    }

    public bool Update(string id, T model)
    {
        throw new System.NotImplementedException();
    }

    public bool Delete(string id)
    {
        throw new System.NotImplementedException();
    }
}

public interface IConcreteClass: ISomeModelAbstract<SomeModel> { }

public class ConcreteClass: SomeModelAbstract<SomeModel>, IConcreteClass { }

2 个答案:

答案 0 :(得分:3)

由于Covariance问题,这不起作用。请考虑此示例代码。

public class SomeModel2: IBasicModel {
    public string _id { get; set; }

    /* some other properties! */
}

之后,您可以将SomeModel2的某个对象传递给x的Save方法,显然,这不行。

    ISomeModelAbstract<IBasicModel> x = new ConcreteClass();
    var m = new SomeModel2();
    x.Save(m);

为了防止这种情况,您应该隐式地告诉您只在返回(out)位置使用通用类型,而不是在输入中。例如:

public interface ISomeModelAbstract<out T> where T: IBasicModel

执行此操作后,遗憾的是,您无法在ISomeModelAbstract界面中使用“保存并更新”方法。因为他们在参数(输入)位置使用T.

有关详细信息,请参阅以下链接:http://tomasp.net/blog/variance-explained.aspx/

答案 1 :(得分:1)

另一个答案已经描述了它在当前状态下不起作用的原因。我想补充一点,在这种情况下,将接口的协变或逆变部分(或两者)提取到单独的接口中通常很有用。例如:

// covariant part, T is used only as return value
// ISomeModelRead is not the best name of course
public interface ISomeModelRead<out T> where T : IBasicModel {
    T GetById(string id);
    IEnumerable<T> GetAll();
}

// the rest of interface, also implementing covariant part
public interface ISomeModelAbstract<T> : ISomeModelRead<T> where T : IBasicModel  {
    bool Save(T model);
    bool Update(string id, T model);
    bool Delete(string id);
}

现在一切都是一样的,除了你能做到:

ISomeModelRead<IBasicModel> x = new ConcreteClass();
x.GetAll();
x.GetById("id");