【问题标题】:C# method is not returning object - method chainingC# 方法不返回对象 - 方法链接
【发布时间】:2017-04-02 20:27:03
【问题描述】:

我在 stackoverflow 上看到了通过链接两种方法提供问题解决方案的帖子。答案看起来像这样:

public x DoThis()
 {
    //do something
    return this; 

 }

public x DoThat ()
{
   //do something else
   return this;

}

var x = new x ().DoThis().DoThat;

我阅读了有关链接方法的信息。但在这种情况下,有些事情似乎不正确。我创建了一个名为 Library 的类,它有两个返回相同类型的不同方法,我可以访问第一个方法,但不能访问第二个。除非,我做错了什么,否则该解决方案是不正确的。

我观看了有关创建集合扩展方法的教程,我想尝试使用这种方法。我不得不承认我还没有完全理解它。所以我想,我应该能够使用 IEnumerable 因为我只是将集合传递给这个类

这是一个类:

class Library
{
    private IEnumerable<Movie> MoviesLibrary;


    public Library(IEnumerable<Movie> library)
    {
        this.MoviesLibrary = library.ToList();

    }

    public IEnumerable<Movie> FindMovie(int _movieId)
    {


        return this.MoviesLibrary.Where(movie => movie.MovieId == _movieId);


    }

    public IEnumerable<Movie> GetByYear(int _year)
    {

        return this.MoviesLibrary.Where(movie => movie.Year == _year);

    }

}

据我了解,“返回此”语句应返回当前实例化的对象。在链式方法中,下一个方法应该使用返回的对象并执行自己的操作。

【问题讨论】:

标签: c# methods chaining


【解决方案1】:

您能否为您给出的第一个代码示例提供更多上下文? (不起作用的那个 - 例如在你的情况下 X 是什么?哪个类有第一个方法?)。

你给出的最后一个例子

public IEnumerable<Movie> GetByYear(int _year)
{
   this.MoviesLibrary.Where(movie => movie.MovieId == _movieId);
    return this;
}

不正确,因为您返回 IEnumerable, 并且 IEnumerable 没有不是扩展方法的附加方法。

扩展方法确实可以帮助你实现你想要的。 实际上,Linq 是作为 IEnumerable 的一组扩展方法实现的

看 https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,577032c8811e20d3

了解更多信息(以及如何编写扩展方法的示例)。

您可能还想在他们的正式 msdn 页面中阅读更多关于扩展方法的信息 https://msdn.microsoft.com/en-us/library/bb383977.aspx

【讨论】:

  • 我认为您缺少一些知识(没关系)-我可以给您一个简短的答案,但是要了解该语言的基础知识,您必须深入研究一些文档(简而言之,c# 是我为 c# 读过的最好的书)...不用担心:)...但是从您编辑的帖子中,我认为最好先阅读语言基础知识...。目前,“库”示例不会甚至编译 - 为了编译它必须实现 IEnumerable ...只有当方法的返回类型也是 Library 并且 Library 是 IEnumerable 时,您想要的链接才会起作用。
  • 我编辑了最后一个方法,它们都返回相同类型的 IEnumerable
【解决方案2】:

这是我自己的问题的解决方案。我不得不承认,从一开始我就不清楚我想要完成什么,这在最初的帖子的编辑中很明显。第一个代码示例是对其他人在此站点上发布的类似问题的回答。它本质上是非常通用的,但是您可以清楚地看到此人试图提倡通过返回相同的对象类型来链接方法。在观看了 Zoran Horvat 的名为“让你的 C# 代码更加面向对象”的教程之后,我想完成类似的事情。本教程可在 Pluralsight 上找到。他的例子是在第 4 章和第 5 章中使用接口和扩展方法。这个解决方案的想法有些相似,但我希望将这个功能包含在单个类中。

我认为整个混淆与该方法需要返回的对象类型有关,以便提供链接功能。让我们看一些简单的字符串示例

someString.ToUpper.ToLower.Trim

我们首先想到的是字符串从一个方法传递到另一个方法,并且在每一步都被该方法修改。因此,我们在处理集合时会遇到类似的情况。

movies.GetByYear(1999).GetByGroup(1).GetByGenre("Action")

在这种情况下,我们从一些通过这个方法链传递的 List 开始。我们也很可能认为这条链中的所有方法都在同一个 List 上运行。毕竟,上一个示例中的字符串属性在所有方法之间共享,即使它正在被修改。这不是这一系列电影实际发生的情况。每种方法都适用于具有不同大小的集合。看起来 GetByYear() 和 GetByGroup() 是处理相同电影列表的方法,它们实际上是具有完全不同列表的独立库对象。

我要感谢 Sokohavi,他对返回 Library 对象发表了评论。他还建议将库对象设为 IEnumerable。不幸的是,如果您认为该方法应该返回 IEnumerable,那么您就走错了路。从技术上讲,Library 是一个 List,但保存 Movie 对象的列表设置为私有,对其他对象不可见。因此没有什么可以迭代的。库对象只有几个方法,如果您选择其中一个,您将无法访问同一类中的其他方法。因此方法必须返回Library 对象才能访问同一类中的所有方法,并且存储Movie 对象的列表必须是IEnumerable。这种方法有一个缺点。您不能在库构造函数中将数据加载到此列表中。相反,数据作为 Parameter 传递。现在,您拥有具有不同电影列表的对象,并且它们之间的通信方式是通过它们的构造函数。

下面我们有 Repository 类,它通过其构造函数将单个项目加载到列表中。库类定义的方法将提供对传递给它的列表的过滤。您还可以创建另一个抽象层来使用该功能。

public class Movie
{
    public string Title { get; set; }
    public int Year { get; set; }
    public int GroupId { get; set; }
    public string Genre { get; set; }

}

public  class Repository
{

    private List<Movie> localDb;

    public Repository()
    {
        localDb = new List<Movie>();


    }


    public IEnumerable<Movie> GetAllMovies()
    {
        localDb = new List<Movie>();

        var movie1 = new Movie() { Title = "Movie1", Year = 2000, GroupId = 1, Genre = "Action" };
        var movie2 = new Movie() { Title = "Movie2", Year = 1999, GroupId = 1, Genre = "Drama" };
        var movie3 = new Movie() { Title = "Movie3", Year = 2000, GroupId = 1, Genre = "Comedy" };
        var movie4 = new Movie() { Title = "Movie4", Year = 2000, GroupId = 2, Genre = "Action" };
        var movie5 = new Movie() { Title = "Movie5", Year = 1999, GroupId = 2, Genre = "Drama" };
        var movie6 = new Movie() { Title = "Movie6", Year = 1999, GroupId = 2, Genre = "Drama" };
        var movie7 = new Movie() { Title = "Movie7", Year = 1999, GroupId = 2, Genre = "Horror" };


        localDb.Add(movie1);
        localDb.Add(movie2);
        localDb.Add(movie3);
        localDb.Add(movie4);
        localDb.Add(movie5);
        localDb.Add(movie6);
        localDb.Add(movie7);


        return localDb;
    }
}

public  class Library
{
    private IEnumerable<Movie> MoviesLibrary;

    public Library(IEnumerable<Movie> movies)
    {

        this.MoviesLibrary = movies.ToList();

    }

    public Library GetByYear(int year)
    {
        return new Library(this.MoviesLibrary.Where(movie => movie.Year == year));


    }

    public Library GetById(int id)
    {
        return new Library(this.MoviesLibrary.Where(movie => movie.GroupId == id));


    }


    public IEnumerable<Movie> GetByGenre(string genre)
    {
        return this.MoviesLibrary.Where(movie => movie.Genre == genre);

    }


    public void Display()           
    {

        foreach (var movie in this.MoviesLibrary)
        {
            Console.WriteLine("Title: {0} , Year {1}, Group: {2}, Genre: {3}", movie.Title,movie.Year,movie.GroupId, movie.Genre);
        }



    }

}

如何使用这些类:

        var repository = new Repository();
        var listOfMovies = repository.GetAllMovies();
        var movies = new Library(listOfMovies);

        var selectedMovies1 = movies.GetByYear(2000).GetById(1).GetByGenre("Action");
        var selectedMovies2 = movies.GetByYear(2000).GetById(2);


       foreach (var movie in selectedMovies1)
        {
            Console.WriteLine("Selected 1 - Title: {0} , Year {1}, Group: {2}, Genre: {3}", movie.Title,movie.Year,movie.GroupId, movie.Genre);



       }

      selectedMovies2.Display();

输出:

选择 1 - 标题:Movie1,2000 年,组:1,类型:动作

标题:Movie4,2000 年,组:2,类型:动作

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多