【问题标题】:Unnecessary cast to IComparer?不必要的转换为 IComparer?
【发布时间】:2011-12-20 10:08:24
【问题描述】:

我了解如何将 IComparer 接口与提供自定义排序方式的帮助类一起使用。例如,这是一个典型的例子,它和我在网上看到的所有例子非常相似,包括微软的在线帮助页面:

// This helper class is used to sort an array of people by name, 
// where 'Person' is a class type.
public class PeopleNameComparer : IComparer
{
  // Test the name of each object.
  int IComparer.Compare(object o1, object o2)
  {
     Person p1 = o1 as Person;
     Person p2 = o2 as Person;
     if (p1 != null && p2 != null)
        return string.Compare(p1.Name, p2.Name);
     else
        throw new ArgumentException("Parameter is not a Person!");
  }
}

我也明白,如果我们有一个 Person (myPeople) 类型的数组,那么我们可以使用以下命令对该数组进行排序:

Array.Sort(myPeople, new PeopleNameComparer());

在这种情况下,我们将创建一个新的 IComparer 类型的 PeopleNameComparer 对象,并将其作为第二个参数传递给 Array.Sort() 方法。

现在为了让事情更整洁,我们可以实现一个属性来为对象用户提供一种更友好的方式来调用自定义排序:

public static IComparer SortByName
{ get { return (IComparer)new PeopleNameComparer(); } }

我不明白这种属性是为什么所有示例都使用 (IComparer) 强制转换将新创建的帮助器类(在此示例中为 PeopleNameComparer)转换为 IComparer 对象,而该对象已经是类型比较器?我试过没有演员表,代码似乎工作正常:

// This property seems to work fine without the cast?
public static IComparer SortByName
{ get { return new PeopleNameComparer(); } }

如果 'new' 关键字返回一个普通的 System.Object 类型,然后必须将其强制转换为适当的 IComparer,我可以理解它,但在这里看不到强制转换的必要性。 但是我按照微软的例子,我的例子和我的 Pro C# 书中的例子很相似。

这里有什么理由需要演员表吗?

【问题讨论】:

  • 你的问题标题和实际问题之间有什么联系?
  • 好的,感谢您更新问题。

标签: c# interface casting


【解决方案1】:

使用显式转换更显式。请原谅这个老生常谈..但仅此而已。它有助于使代码更具可读性。

在某些情况下,如果有多个可能的选项,但在返回类型中似乎不会发生这种情况,显式转换可以帮助运行时消除转换的歧义。仅在表达式中。以下是一个常见示例,您需要在表达式中进行显式转换:

public class StringEnumerable : IEnumerable, IEnumerable<String>
{
    IEnumerator<String> IEnumerable<String>.GetEnumerator()
    {
        yield return "TEST";
    }

    public IEnumerator GetEnumerator()
    {
        // without the explicit cast of `this` to the generic interface the 
        // method would call itself infinitely until a StackOverflowException occurs
        return ((IEnumerable<String>)this).GetEnumerator();
    }
}

如果您从非泛型接口实现中移除显式转换,则会导致无限循环。

【讨论】:

  • 现在有我在等待的非“也许”的答案。 ;p
  • ...但是,我开始想知道,您能举一个演员表模棱两可的例子吗?它是否可读当然是一个见仁见智的问题。我发现该属性的返回类型足够可读。
  • @StevenJeuris - 是的,我想在返回语句中需要显式转换。只有在表达中才会产生歧义。我会修改我的答案以反映这一点。
  • 感谢您的回复,尽管您给出的最后一个示例与我最初的问题不同,但仍然很有趣。
【解决方案2】:

演员表是多余的。

也许在从其他东西重构代码之前可能有必要。

通常,在设计发生变化的较长系统生命周期中,您会看到代码中留下很多绒毛。

当语言功能随时间发生变化(即 C# 自动属性)时,您可能还会看到其他冗余结构。

我认为冗余代码会降低可读性,Resharper 等工具会警告您并帮助您删除它们。

【讨论】:

  • 这就是问题的意思,“也许”不是答案。改为评论?
  • @Steven Jeuris OP 主要询问为什么这些示例包含冗余演员表。可能有很多原因。我希望这实际上比“明确性”的明确演员更有可能。通常,在设计发生变化的较长系统生命周期中,您会看到代码中留下很多绒毛。
  • 您可能有意见,请考虑更新(扩展)您的答案,以便我可以删除反对票。
  • 对于参考书中的示例以及微软自己的示例,代码中的左侧确实不应该有任何绒毛。因为 MS 放了这个看似多余的演员表,我想知道是否有充分的理由?
  • @Yarc 很难知道。你会认为例子是干净的。我知道我通常会把所有多余的东西都去掉。更少的代码意味着更少的错误位置。
【解决方案3】:

如果您的问题仅仅是示例将 PeopleNameComparer 转换为 IComparer 的原因,那么您是正确的,根本没有必要。我想这是为了清楚地向初学者展示结果和界面之间存在隐含的关系。

【讨论】:

  • 我认为这是可能的,虽然属性返回类型应该够清楚了。
  • 我同意。由于没有存储返回值并且 getter 非常简单,我个人认为它不会增加任何清晰度。
【解决方案4】:

我不知道“所有”示例,但确实这两种代码变体应该可以相同地工作。也许他们只是认为显式转换更具可读性。

【讨论】:

    猜你喜欢
    • 2017-06-25
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    • 2010-09-11
    • 2015-11-25
    • 1970-01-01
    • 1970-01-01
    • 2013-08-28
    相关资源
    最近更新 更多