Как приятно применять класс к интерфейсу, который он реализует, но не в момент его создания [дублировать]

TL; DR: Попробуйте использовать Html.Partial вместо Renderpage


Я получал Object reference not set to an instance of an object, когда пытался сделать вид в представлении, отправив ему модель, например это:

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null

Отладка показала, что модель была Null внутри MyOtherView. Пока я не сменил его на:

@{
    MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);

И это сработало.

Кроме того, причина, по которой я не имел Html.Partial для начала, заключалась в том, что Visual Studio иногда выдает ошибки, (f9), если он находится внутри другого построенного цикла foreach, хотя это не ошибка:

@inherits System.Web.Mvc.WebViewPage
@{
    ViewBag.Title = "Entity Index";
    List<MyEntity> MyEntities = new List<MyEntity>();
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
}
<div>
    @{
        foreach(var M in MyEntities)
        {
            // Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?
            @Html.Partial("MyOtherView.cshtml");
        }
    }
</div>

Но я смог запустить приложение без проблем с этим " ошибка". Я смог избавиться от ошибки, изменив структуру цикла foreach, чтобы выглядеть так:

@foreach(var M in MyEntities){
    ...
}

Хотя я чувствую, что это потому, что Visual Studio неправильно интерпретировала амперсанды и скобки .

19
задан Jefim 20 August 2010 в 08:06
поделиться

3 ответа

RepositoryBase<EntityBase> является не базовым классом MyEntityRepository. Вы ищете общую дисперсию , которая существует в C # в ограниченной степени, но здесь не применима.

Предположим, что у вашего класса RepositoryBase<T> был такой метод:

void Add(T entity) { ... }

Теперь рассмотрим:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 
baseRepo.Add(new OtherEntity(...));

Теперь вы добавили другой тип сущности в MyEntityRepository ... и это не может быть прав.

В принципе, общая дисперсия является безопасной только в определенных ситуациях. В частности, общая ковариация (именно это вы описываете здесь) безопасна только тогда, когда вы только когда-либо получаете значения «API»; общая контравариантность (которая работает наоборот) безопасна только тогда, когда вы когда-либо вводили значения в API (например, общее сравнение, которое может сравнивать любые две формы по площади, можно рассматривать как сравнение квадратов).

В C # 4 это доступно для общих интерфейсов и общих делегатов, а не для классов - и только для ссылочных типов. См. MSDN для получения дополнительной информации, прочитайте & lt; plug & gt; прочитайте C # в глубине, 2-е издание , глава 13 & lt; / plug & gt; или блога Эрика Липперта по этой теме. Кроме того, я рассказал об этом в NDC в июле 2010 года - видео доступно здесь .

27
ответ дан Jon Skeet 24 August 2018 в 15:58
поделиться

Всякий раз, когда кто-то задает этот вопрос, я пытаюсь взять их пример и перевести его на что-то, используя более известные классы, которые явно незаконны (это то, что сделал Джон Скит в своем ответе ;

Давайте заменим MyEntityRepository на MyStringList, например:

class MyStringList : List<string> { }

Теперь вам кажется, что вы хотите MyEntityRepository, который должен быть применим к RepositoryBase<EntityBase>, причем рассуждение состоит в том, что это должно быть возможным, поскольку MyEntity происходит от EntityBase.

Но string происходит от object, не Это? Таким образом, по этой логике мы должны были бы сделать MyStringList для List<object>.

Посмотрим, что произойдет, если мы допустим это ...

var strings = new MyStringList();
strings.Add("Hello");
strings.Add("Goodbye");

var objects = (List<object>)strings;
objects.Add(new Random());

foreach (string s in strings)
{
    Console.WriteLine("Length of string: {0}", s.Length);
}

Uh -ой. Внезапно мы перечислим List<string>, и мы наталкиваемся на объект Random. Это нехорошо.

Надеюсь, это затруднит понимание проблемы.

13
ответ дан Community 24 August 2018 в 15:58
поделиться

Это требует ковариации или контравариантности, поддержка которых ограничена в .Net и не может использоваться для абстрактных классов. Вы можете использовать дисперсию на интерфейсах, так что возможным решением вашей проблемы является создание IRepository, который вы используете вместо абстрактного класса.

    public interface IRepository<out T> where T : EntityBase { //or "in" depending on the items.
    }
    public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase {
    }
    public class MyEntityRepository : RepositoryBase<MyEntity> {
    }

    ...

    IRepository<EntityBase> baseRepo = (IRepository<EntityBase>)myEntityRepo;
7
ответ дан Mark H 24 August 2018 в 15:58
поделиться