Почему это неявное приведение невозможно? - PullRequest
0 голосов
/ 07 мая 2018

Учитывая приведенные ниже классы и интерфейсы, мне интересно, почему неявное приведение:

ISomeModelAbstract<IBasicModel> x = new ConcreteClass();

невозможно. Я пытался

public interface ISomeModelAbstract<out T> where T: IBasicModel

Но тогда я не могу использовать GetById и GetAll метод. Я ценю любую помощь или подсказку. Спасибо.

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 голосов
/ 07 мая 2018

Другой ответ уже описывает причину, по которой он не работает в текущем состоянии. Я хочу добавить, что в таких случаях часто бывает полезно выделить ковариантные или контравариантные части вашего интерфейса (или оба) в отдельный интерфейс (ы). Например:

// 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");
0 голосов
/ 07 мая 2018

Это не работает из-за заботы о Ковариантности. Рассмотрим этот пример кода.

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

    /* some other properties! */
}

После этого вы можете передать, например, некоторый объект SomeModel2 в метод Save метода x, и, очевидно, это не так.

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

Чтобы предотвратить это, вы должны неявно сказать, что вы используете свой универсальный тип только в местах возврата (выхода), а не во входных данных. Например:

public interface ISomeModelAbstract<out T> where T: IBasicModel

И после этого, к сожалению, вы не можете использовать метод Save and Update в вашем интерфейсе ISomeModelAbstract. Потому что они используют T в месте ввода параметров.

Для получения дополнительной информации см. Ссылку ниже: http://tomasp.net/blog/variance-explained.aspx/

...