Вернуть тип как IEnumerable вместо просто List? - PullRequest
0 голосов
/ 10 декабря 2018

Я занимаюсь обучением .NET MVC.С учетом сказанного я наткнулся на код, подобный следующему:

public class MoviesController : Controller
{
    public ActionResult Index()
    {
        var movies = GetMovies();

        return View(movies);
    }

    private IEnumerable<Movie> GetMovies()
    {
        return new List<Movie>
        {
            new Movie {Id = 1, Name = "Shrek"},
            new Movie {Id = 2, Name = "LotR"}
        };
    }
}

Индекс просмотра фильмов выглядит следующим образом:

@model IEnumerable<VideoStore.Models.Movie>

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Movies</h2>
<table class="table table-bordered table-hover">
    <thead>
        <tr>
            <th>Movie</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var movie in Model)
        {
            <tr>
                <td>@movie.Name</td>
            </tr>
        }
    </tbody>
</table>

Итак, мой вопрос: почему в MoviesControllerв приватном методе GetMovies() используется тип возврата IEnumerable?Почему бы не использовать тип возврата List?

Ответы [ 5 ]

0 голосов
/ 10 декабря 2018

Возвращаемый тип как IEnumerable вместо просто List?

//controller

var a = new Dictionary<string, string>();
return View(a);

var b = new List<string>();
return View(b);

var c = new LinkedList<string>();
return View(c);

// All work with:

@model IEnumerable<string>

При использовании IEnumerable<> больше Open Принципы sOlid Я редко рекомендую передавать интерфейс или класс любого типа коллекции в представление.

В то время как в C # массивы / коллекции граждан первого класса проблема заключается в том, что они не расширяемы при сохранении Принцип единой ответственности .

Например:

// Controller Returns:
var people = .... as IEnumerable<Person>;
return View(people);

@model IEnumerable<Person>

Теперь предположим, что нужно добавить в представление любую информацию, которая не имеет никакого отношения к группе (например, заголовок).на страницу) .. как ты это делаешь?Вы можете расширить и создать свой собственный класс, производный от IEnumerable<T>, но это нарушает SRP, поскольку заголовок страницы не имеет ничего общего с группой людей.Вместо этого вы должны создать первоклассную модель, которая представляет все, что нужно представлению:

public class MyViewModel
{
  public string Title { get; set;}
  public IEnumerable<Person> People { get; set;}
}

return View(myViewModel);

@model MyViewModel

Я предлагаю всегда делать это.Как только вы начинаете использовать частичные или шаблоны в MVC или хотите отправить обратно тот же объект, становится все труднее отойти от IEnumerable<>, потому что вам нужно изменить Partials и / или Шаблоны и / или Javascript ...

Итак, мой вопрос: почему в MoviesController в приватном методе GetMovies() используется тип возврата IEnumerable<>?

Как правило, это хорошая практика Программа против интерфейса, а не реализация , также называемая Проектирование по контракту (DbC), также известное как программирование по контракту, программирование по контракту и программирование по контракту, .

Это принцип soLid в частности принцип подстановки Лискова.Выдержка:

Подстановочность - это принцип объектно-ориентированного программирования, утверждающий, что в компьютерной программе, если S является подтипом T, объекты типа T могут быть заменены объектами типа S (т.е. объект типа T может быть заменен любым объектом подтипа S) без изменения каких-либо желательных свойств программы (корректность, выполненная задача и т. д.).Более формально, принцип подстановки Лискова (LSP) - это конкретное определение отношения подтипов, называемое (сильным) поведенческим подтипом, которое было впервые введено Барбарой Лисков в основном выступлении на конференции в 1987 году под названием «Абстракция и иерархия данных».Это семантическое, а не просто синтаксическое отношение, поскольку оно призвано гарантировать семантическую совместимость типов в иерархии, в частности, типов объектов.Барбара Лисков и Джаннет Уинг вкратце описали этот принцип в статье 1994 года следующим образом ...

На практике это означает текущий код:

    public ActionResult Index()
    {
        var movies = GetMovies();

        return View(movies);
    }

    private IEnumerable<Movie> GetMovies()
    {
        return new List<Movie>
        {
            new Movie {Id = 1, Name = "Shrek"},
            new Movie {Id = 2, Name = "LotR"}
        };
    }

Может измениться на:

public class MoviesController : Controller
{
    private readonly IMovieDb _movieDb;
    // Dependency Injecting access to movies
    public MoviesController(IMovieDb movieDb)
    {
        _movieDb = movieDb;
    }

    public ActionResult Index()
    {
        var movies = _movieDb .GetMovies();

        return View(movies);
    }
    // ....


public interface IMovieDb
{
    IEnumerable<Movie> GetMovies();
}

Теперь мы понятия не имеем, как извлекаются фильмы ... и нас не должно волновать, пока контракт / интерфейс удовлетворяет наши потребности в данных.

0 голосов
/ 10 декабря 2018

Использование IEnumerable ослабляет связь, что позволяет ему быть напрямую совместимым с другими конкретными типами в случае изменения реализации.Все следующие изменения могут быть сделаны без изменения интерфейса:

//Original
private IEnumerable<Movie> GetMovies()
{
    return new List<Movie>
    {
        new Movie {Id = 1, Name = "Shrek"},
        new Movie {Id = 2, Name = "LotR"}
    };
}

//Using an array
private IEnumerable<Movie> GetMovies()
{
    return new Movie[]
    {
        new Movie {Id = 1, Name = "Shrek"},
        new Movie {Id = 2, Name = "LotR"}
    };
}

//From EF
private IEnumerable<Movie> GetMovies()
{
    return dbContext.Movies.Where( m => Name == "Shrek" || Name == "Lotr );
}


//Covariant
class NerdMovie : Movie {}

private IEnumerable<Movie> GetMovies()
{
    return new List<NerdMovie>
    {
        new NerdMovie {Id = 1, Name = "Shrek"},
        new NerdMovie {Id = 2, Name = "LotR"}
    };
}


//Custom type
class MovieList : List<Movie> { }

private IEnumerable<Movie> GetMovies()
{
    return new MovieList
    {
        new Movie {Id = 1, Name = "Shrek"},
        new Movie {Id = 2, Name = "LotR"}
    };
}

//Using yield
private IEnumerable<Movie> GetMovies()
{
    yield return new Movie {Id = 1, Name = "Shrek"};
    yield return new Movie {Id = 2, Name = "LotR"};
}
0 голосов
/ 10 декабря 2018

IEnumerable<> - это интерфейс, относящийся к итерации по List<>.Возврат List<> немедленно предоставит потребителю GetMovies() больше операций, чем это строго необходимо - например, добавление или удаление из коллекции - что может упростить введение ошибок.Нет технической причины выбирать IEnumerable<> вместо List<>, потому что под капотом он будет вести себя так же.Решение чисто практическое.

0 голосов
/ 10 декабря 2018

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

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

0 голосов
/ 10 декабря 2018

IEnumerable является интерфейсом, поэтому он более гибкий, если вы используете List, вы заставляете метод получения ожидать List.

...