【问题标题】:Why is this implicit cast is not possible?为什么这种隐式强制转换是不可能的?
【发布时间】:2018-05-07 01:46:49
【问题描述】:

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

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 { }

【问题讨论】:

  • 那是因为ISomeModelAbstract&lt;T&gt; 不是关于T 的变体。如果您将其设为ISomeModelAbstract&lt;out T&gt;,那么您可以使用返回T 的方法,但不能使用将T 作为输入参数的方法。它可以是 covariantcontravariant 但不能同时是两者(这是您正在尝试的)。
  • 为此,接口必须是协变的,这意味着泛型类型只会出现,但您有方法将其作为参数输入。
  • 仅仅因为SomeModel is-a IBasicModelSomeModelAbstract&lt;T&gt; is-a ISomeModelAbstract&lt;T&gt;意味着SomeModelAbstract&lt;SomeModel&gt; is-a @987654336 @.

标签: c# types least-common-ancestor


【解决方案1】:

由于协方差问题,这不起作用。考虑这个示例代码。

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);

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

public interface ISomeModelAbstract<out T> where T: IBasicModel

不幸的是,在这样做之后,您将无法在 ISomeModelAbstract 界面中使用 Save 和 Update 方法。因为他们在参数(输入)的地方使用了 T。

更多信息请查看以下链接:http://tomasp.net/blog/variance-explained.aspx/

【讨论】:

    【解决方案2】:

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

    // 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");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-02
      • 2011-12-05
      • 1970-01-01
      • 2015-01-11
      • 1970-01-01
      相关资源
      最近更新 更多