Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 重载函数以接受IEnumerable、ICollection、IList等是否常见(或鼓励)做法。?_C#_.net_Ienumerable_Overloading_Ilist - Fatal编程技术网

C# 重载函数以接受IEnumerable、ICollection、IList等是否常见(或鼓励)做法。?

C# 重载函数以接受IEnumerable、ICollection、IList等是否常见(或鼓励)做法。?,c#,.net,ienumerable,overloading,ilist,C#,.net,Ienumerable,Overloading,Ilist,编辑: 从给出的答案中,我相当清楚地了解到我下面要问的设计应该如何实际实现。考虑到这些建议,并且有人礼貌地指出我的示例代码甚至没有编译,我对以下代码进行了编辑,以反映普遍的共识。从代码的角度来看,剩下的问题可能不再有意义,但我将把它留给子孙后代 假设一个函数有三个重载,一个取IEnumerable,一个取ICollection,一个取IList,如下所示: public static T GetMiddle<T>(IEnumerable<T> values) {

编辑:

从给出的答案中,我相当清楚地了解到我下面要问的设计应该如何实际实现。考虑到这些建议,并且有人礼貌地指出我的示例代码甚至没有编译,我对以下代码进行了编辑,以反映普遍的共识。从代码的角度来看,剩下的问题可能不再有意义,但我将把它留给子孙后代

假设一个函数有三个重载,一个取IEnumerable,一个取ICollection,一个取IList,如下所示:

public static T GetMiddle<T>(IEnumerable<T> values) {
    IList<T> list = values as IList<T>;
    if (list != null) return GetMiddle(list);

    int count = GetCount<T>(values);

    T middle = default(T);
    int index = 0;

    foreach (T value in values) {
        if (index++ >= count / 2) {
            middle = value;
            break;
        }
    }

    return middle;
}

private static T GetMiddle<T>(IList<T> values) {
    int middleIndex = values.Count / 2;
    return values[middleIndex];
}

private static int GetCount<T>(IEnumerable<T> values) {
    // if values is actually an ICollection<T> (e.g., List<T>),
    // we can get the count quite cheaply
    ICollection<T> genericCollection = values as ICollection<T>;
    if (genericCollection != null) return genericCollection.Count;

    // same for ICollection (e.g., Queue<T>, Stack<T>)
    ICollection collection = values as ICollection;
    if (collection != null) return collection.Count;

    // otherwise, we've got to count values ourselves
    int count = 0;
    foreach (T value in values) count++;

    return count;
}
这里的想法是,如果我有一个IList,这会使我的工作更容易;另一方面,我仍然可以使用ICollection甚至IEnumerable来完成这项工作;这些接口的实现没有那么高效

如果运行时能够根据传递的参数选择重载,我不确定这是否可行,但我已经测试过了,它似乎可以


我的问题是:这种方法是否存在我没有想到的问题?或者,这实际上是一个好方法,但是有更好的方法来实现它,可能是尝试首先将values参数强制转换为IList,如果强制转换有效,则运行更有效的重载?我只是想知道其他人的想法。

通常在设计接口时,您希望为参数接受“最小公分母”类型。对于返回类型,这是一个有争议的问题。我通常认为创建上述重载是过分的。它最大的问题是引入了现在必须测试的不需要的代码路径。最好有一种方法以一种方式执行操作,并且在100%的时间内工作。对于上面给定的重载,您可能在行为上存在不一致性,甚至没有意识到这一点,或者更糟糕的是,您可能会意外地在其中一个副本中引入更改,而不是在其他副本中引入更改


如果可以使用IEnumerable,那么使用它;如果不能,那么使用所需的最少接口

通常在设计接口时,您希望为参数接受“最低公分母”类型。对于返回类型,这是一个有争议的问题。我通常认为创建上述重载是过分的。它最大的问题是引入了现在必须测试的不需要的代码路径。最好有一种方法以一种方式执行操作,并且在100%的时间内工作。对于上面给定的重载,您可能在行为上存在不一致性,甚至没有意识到这一点,或者更糟糕的是,您可能会意外地在其中一个副本中引入更改,而不是在其他副本中引入更改


如果可以使用IEnumerable,那么使用它;如果不能,那么使用所需的最少接口

我想说,它不常见,而且可能会令人困惑,因此不太可能是公共API的好选择

您可以接受IEnumerable参数,并在内部检查它是否实际上是ICollection或IList,然后进行相应的优化


这可能对.NET 3.5框架中某些IEnumerable扩展方法中的某些优化有帮助。

我想说这是不常见的,而且可能令人困惑,因此对于公共API来说不太可能是一个好的选择

您可以接受IEnumerable参数,并在内部检查它是否实际上是ICollection或IList,然后进行相应的优化


这可能对.NET 3.5框架中某些IEnumerable扩展方法中的某些优化有帮助。

如果您了解如何使用Reflector实现LINQ扩展方法,您可以看到IEnumerable上的一些扩展方法,例如Count,尝试将序列强制转换为ICollection或IList以优化操作,例如,使用ICollection.Count属性,而不是遍历IEnumerable并对元素进行计数。因此,您最好的选择是接受IEnumerable,然后在ICollection或IList可用的情况下进行此类优化。

如果您了解如何使用Reflector实现LINQ扩展方法,您可以看到IEnumerable上的一些扩展方法,例如Count,尝试将序列强制转换为ICollection或IList以优化操作,例如,使用ICollection.Count属性,而不是遍历IEnumerable并对元素进行计数。因此,最好的选择是接受IEnumerable,然后在ICollection或IList可用的情况下进行此类优化。

我认为一个接受IEnumerable的版本是可行的,并在方法内部检查参数是否是派生的集合类型之一。对于您建议的三个版本,如果有人向您传递编译器静态认为IEnumerable的运行时IList,您将失去效率优势:

我想是一个版本 n接受IEnumerable将是一种方法,并在方法内部检查参数是否是派生的集合类型之一。对于您建议的三个版本,如果有人向您传递编译器静态认为IEnumerable的运行时IList,您将失去效率优势:


不,这确实很少见

无论如何。 由于IList继承自ICollection和IEnumerable,而ICollection继承自IEnumerable,因此您唯一关心的是IEnumerable类型中的性能

我只是认为没有理由以这种方式重载函数,提供不同的签名以实现完全相同的结果,并接受完全相同的类型作为参数无论您是否有IEnumerable或IList,都可以将其传递给三个重载中的任何一个;那只会引起混乱

重载函数时,只是提供一种方法来传递不同类型的参数,如果函数没有该签名,则无法传递给该函数

除非真的有必要,否则不要进行优化。 如果你想优化,做卧底。
你不会为了决定使用哪种方法签名而假装使用你的类的人知道这种优化,对吧?

不。这当然不常见

无论如何。 由于IList继承自ICollection和IEnumerable,而ICollection继承自IEnumerable,因此您唯一关心的是IEnumerable类型中的性能

我只是认为没有理由以这种方式重载函数,提供不同的签名以实现完全相同的结果,并接受完全相同的类型作为参数无论您是否有IEnumerable或IList,都可以将其传递给三个重载中的任何一个;那只会引起混乱

重载函数时,只是提供一种方法来传递不同类型的参数,如果函数没有该签名,则无法传递给该函数

除非真的有必要,否则不要进行优化。 如果你想优化,做卧底。
你不会为了决定使用哪种方法签名而假装使用你的类的人知道这种优化,对吧?

我真的很漠不关心。如果我以你的方式看待它,我一点也不会想到它。但是乔的想法是有价值的。它可能如下所示

public static T GetMiddle<T>(IEnumerable<T> values)
{
  if (values is IList<T>) return GetMiddle((IList<T>)values);
  if (values is ICollection<T>) return GetMiddle((ICollection<T>)values);

  // Use the default implementation here.
}

private static T GetMiddle<T>(ICollection<T> values)
{
}

private static T GetMiddle<T>(IList<T> values)
{
}

我真的很冷漠。如果我以你的方式看待它,我一点也不会想到它。但是乔的想法是有价值的。它可能如下所示

public static T GetMiddle<T>(IEnumerable<T> values)
{
  if (values is IList<T>) return GetMiddle((IList<T>)values);
  if (values is ICollection<T>) return GetMiddle((ICollection<T>)values);

  // Use the default implementation here.
}

private static T GetMiddle<T>(ICollection<T> values)
{
}

private static T GetMiddle<T>(IList<T> values)
{
}

虽然重载方法以接受基类型或派生类型是合法的,但所有其他参数在其他方面都是相同的,但只有在编译器通常能够将后一种形式识别为更好的匹配时,这样做才有好处。因为实现ICollection的对象通常由只需要IEnumerable的代码传递,所以ICollection的实现通常被传递到IEnumerable重载中。因此,IEnumerable重载可能应该检查传入的对象是否实现了ICollection和handle,如果实现了,则应特别检查


如果实现ICollection逻辑的最自然的方式是为它编写一个特殊的方法,那么拥有一个接受ICollection的公共重载,如果给定一个实现ICollection的对象,让IEnumerable重载调用ICollection重载并没有什么特别的错误。让这样一个超载事件公之于众不会增加太多价值,但也不会伤害任何东西。另一方面,在对象同时实现IEnumerable和ICollection,但不实现ICollection的情况下,例如,列表实现IEnumerable和ICollection,但不实现ICollection,可能希望同时使用这两个接口,但如果使用它们的方法中没有任何类型转换,则无法实现这两个接口,或者传递同时使用ICollection引用和IEnumerable引用的方法。后者在公共方法中非常难看,前者将失去重载的好处。

虽然重载方法以接受基类型或派生类型是合法的,但所有其他参数在其他方面都是相同的,只有当编译器通常能够将后一种形式识别为更好的匹配时,这样做才有好处。因为实现ICollection的对象通常由只需要IEnumerable的代码传递,所以ICollection的实现通常被传递到IEnumerable重载中。因此,IEnumerable重载可能应该检查传入的对象是否实现了ICollection和handle,如果实现了,则应特别检查

如果实现ICollection逻辑的最自然的方式是为它编写一个特殊的方法,那么让一个接受ICollection的公共重载和让IEnumerable重载调用ICo并没有什么特别的错误

如果给定一个实现ICollection的对象,则为Collection one。让这样一个超载事件公之于众不会增加太多价值,但也不会伤害任何东西。另一方面,在对象同时实现IEnumerable和ICollection,但不实现ICollection的情况下,例如,列表实现IEnumerable和ICollection,但不实现ICollection,可能希望同时使用这两个接口,但如果使用它们的方法中没有任何类型转换,则无法实现这两个接口,或者传递同时使用ICollection引用和IEnumerable引用的方法。后者在公共方法中非常丑陋,而前者将失去重载的好处。

。。。并在内部检查是否。。。这种方法的唯一问题是它也会创建更多的代码路径。保持简单,只有当探查器告诉您应该作为默认答案时才进行优化。@csharptest.net:这是一个非常大胆的建议,可以不加任何限制地给出,比如假设您没有编写高性能代码—在绝对必要之前不担心性能—在许多情况下都可以,但是,如果性能总是至关重要的,那么你就需要一直考虑它。如果你如此关注性能,那么你可以离开虚拟机!这是另一个讨论话题。@csharptest.net:另一方面,在公共API中有太多重载也有其缺点。@simon:我认为如此关注性能和简单地想高效地做事是有区别的,特别是在很明显如何做的情况下。有时我觉得某些开发人员实际上看不起那些试图将性能牢记在心的人,这太荒谬了。但这也是另一个讨论话题。。。。并在内部检查是否。。。这种方法的唯一问题是它也会创建更多的代码路径。保持简单,只有当探查器告诉您应该作为默认答案时才进行优化。@csharptest.net:这是一个非常大胆的建议,可以不加任何限制地给出,比如假设您没有编写高性能代码—在绝对必要之前不担心性能—在许多情况下都可以,但是,如果性能总是至关重要的,那么你就需要一直考虑它。如果你如此关注性能,那么你可以离开虚拟机!这是另一个讨论话题。@csharptest.net:另一方面,在公共API中有太多重载也有其缺点。@simon:我认为如此关注性能和简单地想高效地做事是有区别的,特别是在很明显如何做的情况下。有时我觉得某些开发人员实际上看不起那些试图将性能牢记在心的人,这太荒谬了。但这也是另一个讨论话题。你说的是有道理的,尽管我确实认为如果提供的参数可以转换为某些类型(如其他答案所建议的),以获得更好的性能,那么使用某些优化是有道理的。在我看来,最好的折衷办法是使最小公分母方法成为唯一的公共方法,并在适当的情况下在内部潜在地利用更多的性能重载。你所说的是有道理的,尽管我确实认为,如果提供的参数可能被转换为某些类型,那么利用某些优化是有道理的,正如其他答案所建议的,为了更好的表现。在我看来,最好的折衷办法是使最小公分母方法成为唯一的公共方法,并在适当的情况下在内部潜在地使用更高性能的重载。这一点很好!我认为你是对的;一个公共方法和可能的多个私有方法重载是最好的选择。这一点很好!我认为你是对的;一个公共方法和可能的多个私有方法重载是可行的方法。“GetMiddle for”IEnumerable和“ICollection as”不会按原样编译,因为并非所有路径都返回值。如果传递了带有索引超出范围异常的零元素列表,则GetMiddle列表将失败。比洛:是的,我知道这一点,不过你提出来是公平的。这些代码示例只是为了说明我的问题。@Dan很抱歉挑三拣四,挑三拣四:这对我来说是一个新的探索领域,我很遗憾地说,我还不清楚仅仅添加return defaultT之类的东西是否能解决所有可能的情况,还不清楚在这些实用方法中处理异常的最佳实践是什么:但也许我应该问一个关于StackOverFlow的问题:best,@BillW:坦白说,我认为返回defaultT没有多大意义;这充其量只是误导,实际上可能会导致错误。我觉得把它放进去很奇怪,但不知道如何满足所有路径返回值的要求。至于需要IList的重载:我实际上认为它应该抛出一个异常g

我输入了不好的信息,这正是你想要了解的原因。也许你有更好的主意?@Dan:曾经是一名技术校对员,但永远是一名技术校对员::你的函数“返回类型现在是”void,我相信这不是你想要的。最好的情况是,“GetMiddle for”IEnumerable和“ICollection原样”不会编译,因为并非所有路径都返回值。如果传递了带有索引超出范围异常的零元素列表,则GetMiddle列表将失败。比洛:是的,我知道这一点,不过你提出来是公平的。这些代码示例只是为了说明我的问题。@Dan很抱歉挑三拣四,挑三拣四:这对我来说是一个新的探索领域,我很遗憾地说,我还不清楚仅仅添加return defaultT之类的东西是否能解决所有可能的情况,还不清楚在这些实用方法中处理异常的最佳实践是什么:但也许我应该问一个关于StackOverFlow的问题:best,@BillW:坦白说,我认为返回defaultT没有多大意义;这充其量只是误导,实际上可能会导致错误。我觉得把它放进去很奇怪,但不知道如何满足所有路径返回值的要求。至于需要IList的重载:我实际上认为,如果输入错误,它应该抛出一个异常,这正是您所关注的原因。也许你有更好的主意?@Dan:曾经是一名技术校对员,但永远是一名技术校对员::你的函数“返回类型现在是”void,我相信这不是你想要的。最好的,是的,这正是我在阅读了其他一些回复后的想法。谢谢是的,这正是我在阅读了其他一些回复后的想法。谢谢请原谅我对你显然非常好的答案的一个可能幼稚的后续回答:当你说如果ICollection或IList可用,那么就进行这种优化,这对我来说意味着可能存在ICollection和IList不可用的情况,我试图想象这些情况会是什么。如果您愿意澄清,我们将不胜感激。谢谢,@BillW:仅仅因为一个对象实现了IEnumerable,它并不意味着它实现了其他任何一个接口。我可以编写自己的类,让您在上面枚举,但不提供添加、包含、删除等。这就是Trillian所说的场景。我正在尝试想象这些情况可能是什么-最常见的情况是在迭代器块中使用yield关键字进行惰性迭代。@BillW:一个很好的例子是如果您正在执行。其中…最后,最后一个得到枚举数,它是Where的结果,它只不过是一个IEnumerable,不能从中得到ICollection。这只是一个例子,不要使用。其中….Last因为有一个重载for Last,它接受一个筛选器委托。请原谅一个可能幼稚的后续回答:当你说如果ICollection或IList可用,那么做这种优化,这对我来说意味着可能存在ICollection和IList不可用的情况,我正在尝试想象这些情况可能是什么。如果您愿意澄清,我们将不胜感激。谢谢,@BillW:仅仅因为一个对象实现了IEnumerable,它并不意味着它实现了其他任何一个接口。我可以编写自己的类,让您在上面枚举,但不提供添加、包含、删除等。这就是Trillian所说的场景。我正在尝试想象这些情况可能是什么-最常见的情况是在迭代器块中使用yield关键字进行惰性迭代。@BillW:一个很好的例子是如果您正在执行。其中…最后,最后一个得到枚举数,它是Where的结果,它只不过是一个IEnumerable,不能从中得到ICollection。这只是一个例子,不要使用。Where….Last,因为有一个接受筛选器委托的重载for Last。