Pregunta Solución para restricción de operador sobrecargado en genéricos .NET


¿Qué haría si quiero tener un método genérico que solo acepte tipos que han sobrecargado a un operador, por ejemplo, el operador de resta? Intenté utilizar una interfaz como restricción, pero las interfaces no pueden tener sobrecarga del operador.

¿Cuál es la mejor manera de lograr esto?


32
2017-09-29 05:37


origen


Respuestas:


No hay una respuesta inmediata; los operadores son estáticos y no pueden expresarse en restricciones, y los primativos existentes no implementan ninguna interfaz específica (contraste con IComparable [<T>] que puede usarse para emular mayor que / menor que).

Sin embargo; si solo quieres que funcione, en .NET 3.5 hay algunas opciones ...

He creado una biblioteca aquí que permite el acceso eficiente y simple a los operadores con genéricos, tales como:

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

Se puede descargar como parte de Miscelánea

Además, en C # 4.0, esto se hace posible a través de dynamic:

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

También tuve (en un momento) una versión de .NET 2.0, pero eso está menos probado. La otra opción es crear una interfaz como

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

etc., pero luego debes pasar un ICalc<T>; a través de todos los métodos, lo cual se complica.


41
2017-09-29 05:46



Descubrí que IL realmente puede manejar esto bastante bien. Ex.

ldarg.0
ldarg.1
add
ret

Compilado en un método genérico, el código se ejecutará bien siempre que se especifique un tipo primitivo. Es posible extender esto para llamar a las funciones del operador en tipos no primitivos.

Ver aquí.


7
2018-06-26 00:00



Hay un código robado de los internautas que uso mucho para esto. Busca o construye usando IL operadores aritméticos básicos. Todo se hace dentro de un Operation<T> clase genérica, y todo lo que tiene que hacer es asignar la operación requerida a un delegado. Me gusta add = Operation<double>.Add.

Se usa así:

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<T> Código fuente cortesía de paste bin: http://pastebin.com/nuqdeY8z

con la atribución a continuación:

/* 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. 
 */

-1
2017-08-18 14:09