【问题标题】:Solution for overloaded operator constraint in .NET generics.NET 泛型中重载运算符约束的解决方案
【发布时间】:2010-09-13 22:36:42
【问题描述】:

如果我想要一个泛型方法只接受重载运算符的类型,例如减法运算符,我会怎么做。我尝试使用接口作为约束,但接口不能有运算符重载。

实现这一目标的最佳方法是什么?

【问题讨论】:

  • 你有一个例子来说明你在哪里尝试使用它吗?我想不出任何有用的地方?
  • 如您所见,根本不可能在接口上定义静态方法,因此您不能将其用作泛型方法的约束。这是一个有点复杂的解决方法:codeproject.com/KB/cs/genericnumerics.aspx 如果您使用的是 .NET 3.5,这也可以通过 LINQ 表达式树来完成,如下所示:rogeralsing.com/2008/02/27/…
  • 一个通用的“Sum”方法就是一个简单的例子。 T Sum(IEnumerable 序列); // 其中 T 有 '+' 运算符
  • 请注意,在 Roger 的博客中,我们讨论/对比了这两种实现(它们非常相似) - 得出的结论是 MiscUtil 代码(之前链接过)更加发达。但他们使用相同的基本方法。

标签: c# generics operator-overloading constraints


【解决方案1】:

没有立即的答案;运算符是静态的,不能在约束中表示 - 现有的原语不实现任何特定的接口(与可用于模拟大于/小于的 IComparable[] 形成对比)。

但是;如果你只是想让它工作,那么在 .NET 3.5 中有一些选项......

我已经建立了一个库 here,它允许使用泛型高效且简单地访问运算符 - 例如:

T result = Operator.Add(first, second); // implicit <T>; here

它可以作为MiscUtil的一部分下载

此外,在 C# 4.0 中,这可以通过dynamic

static T Add<T>(T x, T y) {
    dynamic dx = x, dy = y;
    return dx + dy;
}

我也(曾经)有一个 .NET 2.0 版本,但测试较少。另一种选择是创建一个接口,例如

interface ICalc<T>
{
    T Add(T,T)() 
    T Subtract(T,T)()
} 

等等,但是你需要通过所有方法传递一个ICalc&lt;T&gt;;,这会变得很乱。

【讨论】:

  • 我喜欢在 .NET 4.0 中使用动态的能力,它确实让事情变得更容易。但是,值得指出的是,使用它会对性能产生影响,因为它必须在运行时做更多的工作。我很想知道它有多大的影响,我认为它需要一些基准测试。
  • 基准测试已经完成;试试这里的代码:social.msdn.microsoft.com/Forums/en-US/vs2010ctpvbcs/thread/…
  • 我试用了您的库(适用于 .NET 3.5),但我有一个问题:为什么以下行不起作用:MiscUtil.Operator.Add("A", "B"); .据我了解,它应该返回“AB”。
  • @Malki - 好吧,我们可以添加它,但这并不是真正的算术运算。严格来说,它并不是真正定义的运算符 - 当前是 编译器(不是类型本身)为字符串提供 + 的含义...
  • 如果要转换为动态,使用泛型有什么意义吗?为什么不让参数和返回码也动态化呢?以这种方式混合泛型和动态是否有优势?
【解决方案2】:

我发现 IL 实际上可以很好地处理这个问题。前任。

ldarg.0
ldarg.1
add
ret

在泛型方法中编译,只要指定了原始类型,代码就可以正常运行。可以扩展它以在非原始类型上调用运算符函数。

here

【讨论】:

    【解决方案3】:

    有一段从内部人员那里窃取的代码,我为此经常使用。它使用IL 基本算术运算符查找或构建。这一切都在Operation&lt;T&gt; 泛型类中完成,您所要做的就是将所需的操作分配给委托。喜欢add = Operation&lt;double&gt;.Add

    它是这样使用的:

    public struct MyPoint
    {
        public readonly double x, y;
        public MyPoint(double x, double y) { this.x=x; this.y=y; }
        // User types must have defined operators
        public static MyPoint operator+(MyPoint a, MyPoint b)
        {
            return new MyPoint(a.x+b.x, a.y+b.y);
        }
    }
    class Program
    {
        // Sample generic method using Operation<T>
        public static T DoubleIt<T>(T a)
        {
            Func<T, T, T> add=Operation<T>.Add;
            return add(a, a);
        }
    
        // Example of using generic math
        static void Main(string[] args)
        {
            var x=DoubleIt(1);              //add integers, x=2
            var y=DoubleIt(Math.PI);        //add doubles, y=6.2831853071795862
            MyPoint P=new MyPoint(x, y);
            var Q=DoubleIt(P);              //add user types, Q=(4.0,12.566370614359172)
    
            var s=DoubleIt("ABC");          //concatenate strings, s="ABCABC"
        }
    }
    

    Operation&lt;T&gt;源代码由paste bin提供:http://pastebin.com/nuqdeY8z

    注明出处如下:

    /* Copyright (C) 2007  The Trustees of Indiana University
     *
     * Use, modification and distribution is subject to the Boost Software
     * License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
     * http://www.boost.org/LICENSE_1_0.txt)
     *  
     * Authors: Douglas Gregor
     *          Andrew Lumsdaine
     *          
     * Url:     http://www.osl.iu.edu/research/mpi.net/svn/
     *
     * This file provides the "Operations" class, which contains common
     * reduction operations such as addition and multiplication for any
     * type.
     *
     * This code was heavily influenced by Keith Farmer's
     *   Operator Overloading with Generics
     * at http://www.codeproject.com/csharp/genericoperators.asp
     *
     * All MPI related code removed by ja72. 
     */
    

    【讨论】:

    • 哦,我只看到dynamic关键字解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多