Решение для перегруженного операторного ограничения в.NET generics
Что бы я сделал, если бы я хотел иметь универсальный метод, который принимает только типы, которые перегружены оператором, например оператором вычитания. Я пытался использовать интерфейс в качестве ограничения, но интерфейсы не могут иметь перегрузку операторов.
Каков наилучший способ достичь этого?
4 ответа
Нет немедленного ответа; операторы являются статическими и не могут быть выражены в ограничениях - а существующие примитивы не реализуют какой-либо конкретный интерфейс (в отличие от IComparable [
Тем не мение; если вы просто хотите, чтобы это работало, то в.NET 3.5 есть несколько вариантов...
Я собрал здесь библиотеку, которая обеспечивает эффективный и простой доступ к операторам с обобщениями - такими как:
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<T>;
через все методы, которые становятся грязными.
Я обнаружил, что ИЛ действительно может справиться с этим довольно хорошо. Ex.
ldarg.0
ldarg.1
add
ret
Скомпилированный в универсальном методе, код будет работать нормально, пока указан примитивный тип. Может быть возможно расширить это, чтобы вызвать операторные функции на не примитивных типах.
Смотрите здесь.
Вы можете решить эту проблему, используя делегат вместо ограничения интерфейса.
public class Example
{
public static T Add<T>(T left, T right, Func<T, T, T> addFunc) =>
addFunc(left, right);
}
Определите метод, который принимает делегат в качестве параметра, и используйте его следующим образом.
var result = Example.Add(10, 20, (x, y) => x + y);
Есть фрагмент кода, украденный из интернатов, который я часто использую для этого. Он ищет или строит используя IL
основные арифметические операторы. Это все сделано в течение Operation<T>
универсальный класс, и все, что вам нужно сделать, это назначить требуемую операцию в делегат. подобно add = Operation<double>.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<T>
Исходный код любезно предоставлен вставкой 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.
*/