【问题标题】:Unnecessary cast to interface?不必要的接口转换?
【发布时间】:2014-07-09 11:53:06
【问题描述】:

我正在使用工厂创建IComparer<User> 对象来对用户列表进行排序。

我有 2 个类:AscendingDescending,都实现了 IComparer<User>。这是代码:

namespace Test
{
    public class Program
    {
        public static void Main(string[] args)
        {
            List<User> users = new List<User>();
            users.Add(new User("foo", "bar"));
            // ...
            IComparer<User> cmp = ComparerFactory.GetComparer("FirstName", true);
            if (cmp != null)
            {
                users.Sort(cmp);
            }
        }
    }

    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public User(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }
    }

    public class UserFirstNameComparer
    {
        public class Ascending : IComparer<User>
        {
            public int Compare(User u1, User u2)
            {
                return String.Compare(u1.FirstName, u2.FirstName, true);
            }
        }

        public class Descending : IComparer<User>
        {
            public int Compare(User u1, User u2)
            {
                return new UserFirstNameComparer.Ascending().Compare(u1, u2) * -1;
            }
        }
    }

    public static class ComparerFactory
    {
        public static IComparer<User> GetComparer(string fieldName, bool ascending)
        {
            switch (fieldName)
            {
                case "FirstName":
                    return ascending ?
                        new UserFirstNameComparer.Ascending() : // ERROR IS HERE
                        new UserFirstNameComparer.Descending();
                 //...
            }
            return null;
        }
    }

但我收到一个错误(行:new UserFirstNameComparer.Ascending() :):

Type of conditional expression cannot be determined because there is no implicit conversion between 'Test.UserFirstNameComparer.Ascending' and 'Test.UserFirstNameComparer.Descending'

我不明白这是什么意思,两者都是 IComparer 对象,那么有什么问题? 奇怪的是,我可以通过(不必要的?)演员来修复错误:

// This works
return ascending ?
    (IComparer<User>) new UserFirstNameComparer.Ascending() :
    new UserFirstNameComparer.Descending();


// This works too
return ascending ?
    new UserFirstNameComparer.Ascending() :
    (IComparer<User>) new UserFirstNameComparer.Descending();

当然,当我在这两种情况下施放时它都有效。但我不明白为什么它只适用于一个演员表,以及为什么没有演员表时它不起作用。有什么想法吗?

(我使用的是 VS 2012,.NET v4.0.30319)

【问题讨论】:

  • 查看这个问题的答案:stackoverflow.com/questions/202271/…
  • 与其拥有两个Comparers(这很愚蠢),不如只创建一个常规Comparer(升序)并执行 list.Sort(cmp).Reverse() 或list.OrderBy(cmp) / list.OrderByDescending(cmp)?
  • 请注意,如果Compare 调用的返回值为-2147483648,则如上所述乘以-1 将无法正常工作。一些Compare 实现确实返回了除-10+1 之外的其他值,并且被允许这样做。

标签: c# interface casting


【解决方案1】:

表达式

cond ? X : Y

只是要求(简化)X 的编译时类型可以隐式转换为Y 的编译时类型,反之亦然。它不会搜索这两种类型的所有接口和基类来尝试找到它们的一些“通用”类型。 (就算有,多通用接口的情况下怎么处理?)

这就是语言的设计方式。方法的返回类型是什么无关紧要,在尝试解析 ?: 表达式时无法考虑。

您已经找到了解决方案,将X 和/或Y 明确地转换为所需的类型。


在回答了您的问题后,我还建议您如何在不编写 AscendingDescending 的两个类的情况下做到这一点。您可以像这样创建IComparer&lt;&gt;

return ascending
  ? Comparer<User>.Create((u1, u2) => String.Compare(u1.FirstName, u2.FirstName, true))
  : Comparer<User>.Create((u1, u2) => String.Compare(u2.FirstName, u1.FirstName, true))
  ;

所以不需要(可能)你的这两个班级。

【讨论】:

  • 感谢您的解释和建议。效果很好!
  • 我应该提一下 Comparer&lt;&gt;.Create method 是 .NET 4.5 (2012) 中的新内容。自 .NET 2.0 (2005) 以来,Comparison&lt;&gt; 类型的委托就已存在。
【解决方案2】:

new UserFirstNameComparer.Ascending() 返回Ascending 的实例。 new UserFirstNameComparer.Descending() 返回一个 Descending 的实例。

尽管两者都实现了IComparer,但这些实例的类型是不同的。当您将其中一个转换为IComparer 时,生成的对象的类型为IComparer。现在可以进行隐式转换,因为一个是接口,另一个是实现它的类。

引用文档,用于条件表达式

condition ?: first_expression : second_expression

first_expressionsecond_expression 的类型必须相同,或者必须存在从一种类型到另一种类型的隐式转换。

【讨论】:

    猜你喜欢
    • 2017-06-25
    • 2011-12-20
    • 1970-01-01
    • 2010-09-11
    • 2010-10-11
    • 2015-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多