Pregunta

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

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

¿Fue útil?

Solución

No hay respuesta inmediata; los operadores son estáticos y no pueden expresarse en restricciones, y las primitivas 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 reunido una biblioteca aquí que permite eficiente y acceso simple a operadores con genéricos, como:

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

Se puede descargar como parte de MiscUtil

Además, en C # 4.0, esto es 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 .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 debe pasar un ICalc<T>; a través de todos los métodos, lo que se vuelve complicado.

Otros consejos

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 funcionará bien siempre que se especifique un tipo primitivo. Es posible extender esto para llamar a funciones de operador en tipos no primitivos.

Ver aquí .

Hay un código robado de los internos que uso mucho para esto. Busca o construye utilizando IL operadores aritméticos básicos. Todo se realiza dentro de una clase genérica Operation<T>, 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"
    }
}

<=> 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. 
 */
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top