【问题标题】:Can I specify "or" with keyword "where" an Generic Type Parameters Constraints?我可以用关键字“where”指定“或”泛型类型参数约束吗?
【发布时间】:2015-07-17 08:07:14
【问题描述】:

我有一个函数可以进行加扰比较

    public static int ScrambledEquals<TKey, T>(
      IDictionary<TKey, T> list1, 
      IDictionary<TKey, T> list2)
        where TKey : IComparable
        where T : ICompareAsHtml or IComparable // compilation failure on this line
    {
      // ...
    }

    public static int ScrambledEquals<TKey, T>(
      IDictionary<TKey, T> list1, 
      IDictionary<TKey, T> list2)
        where TKey : IComparable
        where T : ICompareAsHtml {} // This works!

TKey 必须是 IComparable,但值类型 T 可以是 IComparable 或实现下面的接口

    public interface ICompareAsHtml
    {
      // Compare current "this" object with "obj"
      // and persist the difference in html somewhere
      // return number of differences.
      int compareAsHtml(object obj);
    }

如何更新 where 行以使编译通过(不删除它,不使用其中一个接口)?

或者更好的是,您可以阅读下面的代码以了解我的情况。我需要使用下面的函数来比较一个字符串集合,它是 IComparable;或者比较一个拥抱类的集合,比如销售,它实现了 ICompareAsHtml 但没有实现 IComparable。 (因为太多的属性使得这个类很难实现 CompareTo 功能 - 无法给出“一个”数字来表示两个实例之间的“方向”和“距离”。)

    public static int ScrambledEquals<TKey, T>(
      IDictionary<TKey, T> list1,
      IDictionary<TKey, T> list2)
    where TKey : IComparable
    // where T : ICompareAsHtml or IComparable
    // commented out above line to make compilation pass
    {
        int nDIff = 0;
        List<TKey> bothKeys = list1.Keys.Union<TKey>(list2.Keys).ToList();
        bothKeys.Sort();
        foreach (TKey key in bothKeys)
        {
            // code omitted - not related to this question 
            {
                    // key exist in both lists.
                    object o1 = list1[key];
                    object o2 = list2[key];
                    if (o1 is IComparable && o2 is IComparable)
                    {
                        IComparable v1 = (IComparable)o1;
                        IComparable v2 = (IComparable)o2;
                        if (0 != v1.CompareTo(v2))
                        {
                            nDIff++;
                            // Save the difference in html
                        }
                    }
                    else if (o1 is ICompareAsHtml && o2 is ICompareAsHtml)
                    {
                        ICompareAsHtml v1 = (ICompareAsHtml)o1;
                        ICompareAsHtml v2 = (ICompareAsHtml)o2;
                        // Save the difference
                        nDIff += v1.compareAsHtml(v2);
                    }
                    else
                    {
                        // If I can use where keyword
                        // I don't need this exception
                        throw new Exception(@"Error: Program error
    - Value Type is neither IComparable nor ICompareAsHtml.");
                    }
                }
            }
        }
        return nDIff;
    }

欢迎所有建议/反馈/cmets!

【问题讨论】:

    标签: c#


    【解决方案1】:

    这不是框架支持的东西。但是,这样做的需要意味着缺少IComparable 和ICompareAsHtml 都应该实现的公共接口,而您可以限制使用该接口。

    这里的坏消息是IComparable 是框架的一部分,这意味着您无法更改它来实现缺少的接口。好消息是IComparable 已经很简单了……也许它是那个缺失的界面。

    ICompareAsHtml 不是框架的一部分,文档中也没有提到我目前可以通过 Google 搜索的任何产品,这意味着它可能是一个您可以更改的界面。如果是这样,只需将ICompareAsHtml 实现IComparable 并约束为IComparable,您将满足您的条件

    【讨论】:

    • 这个界面是我的问题。使用一个函数 compareAsHtml()。
    • 啊...我在问题中间错过了它。那么,您所要做的就是使该接口也继承/实现 IComparable,然后您就可以限制为 IComparable。
    • 查看我对 Gabe Perez 回答的评论。我想要其中一个,而不是两个。
    • ICompareAsHtml 逻辑暗示 IComparable...只需将IComparable所需的CompareTo()方法调用compareAsHtml()即可得到结果。这将为您提供所需的一切。
    • 对于像 Sales 这样的拥抱类,实现 CompareTo() 非常困难,但可以为其值类型实现 CompareAsHtml 并为其每个集合再次调用此函数。
    【解决方案2】:

    您不能对约束列表执行 OR,请查看此处的文档: https://msdn.microsoft.com/en-us/library/d5x73970.aspx

    也许你可以让你的接口派生自 IComparable:

    public interface ICompareAsHtml : IComparable
    {
        int compareAsHtml(object obj);
    }
    
    class CompareAsHtml : ICompareAsHtml
    {
        public int CompareTo(object obj)
        {
            return compareAsHtml(obj);
        }
    
        public int compareAsHtml(object obj)
        {
            //do the core comparison here and return
        }
    }
    

    【讨论】:

    • 不。你可以从我的代码中看到,如果 value 类型实现 compareAsHtml() 函数,则该函数不需要来自类型 T 的任何更多信息。或者,如果这个值类型T可以比较,我们可以在这里比较,并将差异持久化在html中。
    • 我已经更新了我的 sn-p 以提供更多详细信息。在这种情况下,任何 ICompareAsHtml 也是 IComparable,因此您只需要 IComparable 约束。
    • 嗯...不完全是我想要的。 Type T可以是IComparable,比如string,这是我的一种情况。在其他情况下,ToString() 对于 Sales 这样的大类并不容易实现,然后使用 ICompareAsHtml 来实现 compareAsHtml() 函数。所以我希望类型 T 可以实现 ICompareAsHtml 或 IComparable 之一,而不是两者都需要。简单来说,我想要这两个接口之间的 OR 关系,而不是它们的 AND 关系。正如您在开始时发现的那样,似乎无法从 msdn 网站这样做。
    【解决方案3】:

    最后,我想出使用两个函数而不是一个函数

    public static int ScrambledEqualsComparable<TKey, T>(
      IDictionary<TKey, T> list1, 
      IDictionary<TKey, T> list2)
        where TKey : IComparable
        where T : IComparable
    {
      return ScrambledEquals(list1, list2);
    }
    
    public static int ScrambledEqualsCompareAsHtml<TKey, T>(
      IDictionary<TKey, T> list1, 
      IDictionary<TKey, T> list2)
        where TKey : IComparable
        where T : ICompareAsHtml 
    {
      return ScrambledEquals(list1, list2);
    }
    
    private static int ScrambledEquals<TKey, T>(
      Dictionary<TKey, T> list1, 
      Dictionary<TKey, T> list2)
        where TKey : IComparable
    {
      // the same as above code - No need to throw exception as 
      // it has to be one of ICompareAsHtml or IComparable.
    }
    

    当然,我可以将 ScrambledEqualsCompareAsHtml、ScrambledEqualsComparable 重命名为专有名称。但是两个函数可以解决这个问题。确保旧功能更改为私有,以避免从外部直接访问。

    你觉得这个解决方案怎么样?

    【讨论】:

    • 如果把最后一个方法设为私有,就不用担心抛出了。 (删除了关于在前两个中摆脱 T 的早期评论——最近阅读了关于 co[ntra] 方差并认为这是可能的。)
    • 太棒了。喜欢你的想法。
    猜你喜欢
    • 2011-12-11
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多