tipos tipo ser referencia programacion metodos interfaces genericos genericas debe datos conversion constructores colecciones c# generics constraints

tipo - metodos genericos c#



¿Hay alguna restricción que restrinja mi método genérico a tipos numéricos? (18)

¿Cuál es el punto del ejercicio?

Como la gente ya señaló, podría tener una función no genérica que ocupe el elemento más grande, y el compilador convertirá automáticamente las entradas más pequeñas para usted.

static bool IntegerFunction(Int64 value) { }

Si su función está en una ruta crítica de rendimiento (muy poco probable, IMO), podría proporcionar sobrecargas para todas las funciones necesarias.

static bool IntegerFunction(Int64 value) { } ... static bool IntegerFunction(Int16 value) { }

¿Alguien puede decirme si hay una forma con genéricos de limitar un argumento de tipo genérico a solo:

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

Soy consciente de where palabra clave, pero no puedo encontrar una interfaz solo para estos tipos,

Algo como:

static bool IntegerFunction<T>(T value) where T : INumeric


Desafortunadamente, solo puede especificar la estructura en la cláusula where en esta instancia. Parece extraño que no pueda especificar Int16, Int32, etc. específicamente, pero estoy seguro de que hay una razón de implementación profunda que subyace a la decisión de no permitir tipos de valor en una cláusula where.

Supongo que la única solución es hacer una verificación de tiempo de ejecución que desafortunadamente evita que el problema sea detectado en el momento de la compilación Eso sería algo así como:

static bool IntegerFunction<T>(T value) where T : struct { if (typeof(T) != typeof(Int16) && typeof(T) != typeof(Int32) && typeof(T) != typeof(Int64) && typeof(T) != typeof(UInt16) && typeof(T) != typeof(UInt32) && typeof(T) != typeof(UInt64)) { throw new ArgumentException( string.Format("Type ''{0}'' is not valid.", typeof(T).ToString())); } // Rest of code... }

Lo que es un poco feo, lo sé, pero al menos proporciona las restricciones requeridas.

También me gustaría investigar las posibles implicaciones de rendimiento para esta implementación, tal vez haya una manera más rápida de hacerlo.


Esta limitación me afectó cuando intenté sobrecargar operadores para tipos genéricos; ya que no había una restricción "INumérica", y por una serie de otras razones que las personas buenas en están felices de proporcionar, las operaciones no se pueden definir en tipos genéricos.

Quería algo como

public struct Foo<T> { public T Value{ get; private set; } public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS) { return new Foo<T> { Value = LHS.Value + RHS.Value; }; } }

He resuelto este problema utilizando la escritura en tiempo de ejecución dinámico .net4.

public struct Foo<T> { public T Value { get; private set; } public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS) { return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value }; } }

Las dos cosas sobre el uso dynamic son

  1. Actuación. Todos los tipos de valor son recuadrados.
  2. Errores de tiempo de ejecución. Usted "vence" al compilador, pero pierde seguridad de tipo. Si el tipo genérico no tiene el operador definido, se lanzará una excepción durante la ejecución.

Esta pregunta es un poco de una pregunta frecuente, por lo que estoy publicando esto como wiki (ya que he publicado similares anteriormente, pero esta es una más antigua); de todas formas...

¿Qué versión de .NET estás usando? Si está utilizando .NET 3.5, entonces tengo una yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html en MiscUtil (gratis, etc.).

Esto tiene métodos como T Add<T>(T x, T y) y otras variantes para la aritmética en diferentes tipos (como DateTime + TimeSpan ).

Además, esto funciona para todos los operadores incorporados, levantados y personalizados, y almacena en caché al delegado para el rendimiento.

Algunos antecedentes adicionales sobre por qué esto es complicado están here .

También es posible que desee saber que dynamic clase dynamic (4.0) resuelve este problema indirectamente también, es decir,

dynamic x = ..., y = ... dynamic result = x + y; // does what you expect


He creado una pequeña funcionalidad de biblioteca para resolver estos problemas:

En lugar de:

public T DifficultCalculation<T>(T a, T b) { T result = a * b + a; // <== WILL NOT COMPILE! return result; } Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

Podrías escribir:

public T DifficultCalculation<T>(Number<T> a, Number<T> b) { Number<T> result = a * b + a; return (T)result; } Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

Puede encontrar el código fuente aquí: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number


Hejlsberg ha descrito las razones para no implementar la función en una entrevista con Bruce Eckel.

Sin embargo, debo admitir que no sé cómo cree que funcionará su solución propuesta. Su propuesta es diferir las operaciones aritméticas a alguna otra clase genérica (¡lea la entrevista!). ¿Cómo ayuda esto? En mi humilde opinión, no mucho.


Los tipos primitivos numéricos .NET no comparten ninguna interfaz común que les permita ser utilizados para cálculos. Sería posible definir sus propias interfaces (por ejemplo, ISignedWholeNumber ) que realizarían dichas operaciones, definir estructuras que contienen un solo Int16 , Int32 , etc. e implementar esas interfaces, y luego tener métodos que acepten tipos genéricos restringidos a ISignedWholeNumber , pero que tengan convertir valores numéricos a sus tipos de estructura probablemente sería una molestia.

Un enfoque alternativo sería definir la clase estática Int64Converter<T> con una propiedad estática bool Available {get;}; y delegados estáticos para Int64 GetInt64(T value) , T FromInt64(Int64 value) , bool TryStoreInt64(Int64 value, ref T dest) . El constructor de la clase podría usar un código fijo para cargar delegados para tipos conocidos, y posiblemente usar Reflection para probar si el tipo T implementa métodos con los nombres y firmas apropiados (en caso de que sea algo así como una estructura que contenga un Int64 y represente un número, pero tiene un método ToString() personalizado. Este enfoque perdería las ventajas asociadas con la verificación de tipos en tiempo de compilación, pero aún así se las arreglaría para evitar las operaciones de boxeo y cada tipo solo tendría que "comprobarse" una vez. Después de eso, las operaciones asociadas con ese tipo serían reemplazadas por un despacho delegado.


Me preguntaba lo mismo que Samjudson, ¿por qué solo para enteros? y si ese es el caso, es posible que desee crear una clase auxiliar o algo así para mantener todos los tipos que desee.

Si todo lo que quieres son números enteros, no uses un genérico, eso no es genérico; O mejor aún, rechace cualquier otro tipo marcando su tipo.


No hay forma de restringir las plantillas a los tipos, pero puede definir diferentes acciones según el tipo. Como parte de un paquete numérico genérico, necesitaba una clase genérica para agregar dos valores.

class Something<TCell> { internal static TCell Sum(TCell first, TCell second) { if (typeof(TCell) == typeof(int)) return (TCell)((object)(((int)((object)first)) + ((int)((object)second)))); if (typeof(TCell) == typeof(double)) return (TCell)((object)(((double)((object)first)) + ((double)((object)second)))); return second; } }

Tenga en cuenta que los tipos de datos se evalúan en el momento de la compilación, por lo que el compilador eliminará las sentencias if. El compilador también elimina los lanzamientos espurios. Así que algo se resolvería en el compilador para

internal static int Sum(int first, int second) { return first + second; }


No hay ninguna restricción para esto. Es un problema real para cualquier persona que quiera usar genéricos para cálculos numéricos.

Iría más allá y diría que necesitamos

static bool GenericFunction<T>(T value) where T : operators( +, -, /, * )

O incluso

static bool GenericFunction<T>(T value) where T : Add, Subtract

Desafortunadamente, solo tiene interfaces, clases base y la struct palabras clave (debe ser de tipo de valor), class (debe ser de tipo de referencia) y new() (debe tener el constructor predeterminado)

Podría ajustar el número en otra cosa (similar a INullable<T> ) como aquí en el proyecto de código .

Podría aplicar la restricción en tiempo de ejecución (reflexionando para los operadores o verificando los tipos) pero eso pierde la ventaja de tener el genérico en primer lugar.


No hay una solución ''buena'' para esto todavía. Sin embargo, puede restringir el argumento de tipo de manera significativa para descartar muchas fallas para su restricción hipotética "INumérica" ​​como Haacked ha mostrado anteriormente.

static bool IntegerFunction <T> (valor T) donde T: IComparable, IFormattable, IConvertible, IComparable <T>, IEquatable <T>, struct {...


Probablemente lo más cercano que puedes hacer es

static bool IntegerFunction<T>(T value) where T: struct

No estoy seguro si podrías hacer lo siguiente

static bool IntegerFunction<T>(T value) where T: struct, IComparable , IFormattable, IConvertible, IComparable<T>, IEquatable<T>

Para algo tan específico, ¿por qué no tener sobrecargas para cada tipo, la lista es tan corta y posiblemente tendría menos espacio de memoria?


Si está utilizando .NET 4.0 y versiones posteriores, puede usar dinámico como argumento del método y verificar en tiempo de ejecución que el tipo de argumento dinámico pasado sea numérico / entero.

Si el tipo de la dinámica pasada no es numérico / entero, entonces lance la excepción.

Un ejemplo de código corto que implementa la idea es algo como:

using System; public class InvalidArgumentException : Exception { public InvalidArgumentException(string message) : base(message) {} } public class InvalidArgumentTypeException : InvalidArgumentException { public InvalidArgumentTypeException(string message) : base(message) {} } public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException { public ArgumentTypeNotIntegerException(string message) : base(message) {} } public static class Program { private static bool IntegerFunction(dynamic n) { if (n.GetType() != typeof(Int16) && n.GetType() != typeof(Int32) && n.GetType() != typeof(Int64) && n.GetType() != typeof(UInt16) && n.GetType() != typeof(UInt32) && n.GetType() != typeof(UInt64)) throw new ArgumentTypeNotIntegerException("argument type is not integer type"); //code that implements IntegerFunction goes here } private static void Main() { Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method. Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here. Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method. }

Por supuesto que esta solución funciona solo en tiempo de ejecución pero nunca en tiempo de compilación.

Si desea una solución que siempre funcione en tiempo de compilación y nunca en tiempo de ejecución, tendrá que envolver la dinámica con una estructura / clase pública cuyos constructores públicos sobrecargados solo acepten argumentos de los tipos deseados y le den el nombre apropiado a la estructura / clase.

Tiene sentido que la dinámica envuelta sea siempre miembro privado de la clase / estructura y que sea el único miembro de la estructura / clase y que el nombre del único miembro de la estructura / clase sea "valor".

También tendrá que definir e implementar métodos públicos y / o operadores que trabajen con los tipos deseados para el miembro dinámico privado de la clase / estructura, si es necesario.

También tiene sentido que la estructura / clase tenga un constructor especial / único que acepte dinámico como argumento que inicializa que es solo un miembro dinámico privado llamado "valor", pero el modificador de este constructor es privado, por supuesto.

Una vez que la clase / estructura esté lista, defina el tipo de IntegerFunction del argumento para que sea la clase / estructura que se ha definido.

Un ejemplo de código largo que implementa la idea es algo como:

using System; public struct Integer { private dynamic value; private Integer(dynamic n) { this.value = n; } public Integer(Int16 n) { this.value = n; } public Integer(Int32 n) { this.value = n; } public Integer(Int64 n) { this.value = n; } public Integer(UInt16 n) { this.value = n; } public Integer(UInt32 n) { this.value = n; } public Integer(UInt64 n) { this.value = n; } public Integer(Integer n) { this.value = n.value; } public static implicit operator Int16(Integer n) { return n.value; } public static implicit operator Int32(Integer n) { return n.value; } public static implicit operator Int64(Integer n) { return n.value; } public static implicit operator UInt16(Integer n) { return n.value; } public static implicit operator UInt32(Integer n) { return n.value; } public static implicit operator UInt64(Integer n) { return n.value; } public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); } public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); } public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); } public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); } public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); } public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); } public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); } public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); } public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); } public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); } public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); } public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); } public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); } public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); } public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); } public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); } public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); } public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); } public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); } public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); } public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); } public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); } public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); } public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); } public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); } public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); } public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); } public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); } public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); } public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); } public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); } public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); } public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); } public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); } public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); } public static bool operator ==(Integer x, Int16 y) { return x.value == y; } public static bool operator !=(Integer x, Int16 y) { return x.value != y; } public static bool operator ==(Integer x, Int32 y) { return x.value == y; } public static bool operator !=(Integer x, Int32 y) { return x.value != y; } public static bool operator ==(Integer x, Int64 y) { return x.value == y; } public static bool operator !=(Integer x, Int64 y) { return x.value != y; } public static bool operator ==(Integer x, UInt16 y) { return x.value == y; } public static bool operator !=(Integer x, UInt16 y) { return x.value != y; } public static bool operator ==(Integer x, UInt32 y) { return x.value == y; } public static bool operator !=(Integer x, UInt32 y) { return x.value != y; } public static bool operator ==(Integer x, UInt64 y) { return x.value == y; } public static bool operator !=(Integer x, UInt64 y) { return x.value != y; } public static bool operator ==(Integer x, Integer y) { return x.value == y.value; } public static bool operator !=(Integer x, Integer y) { return x.value != y.value; } public override bool Equals(object obj) { return this == (Integer)obj; } public override int GetHashCode() { return this.value.GetHashCode(); } public override string ToString() { return this.value.ToString(); } public static bool operator >(Integer x, Int16 y) { return x.value > y; } public static bool operator <(Integer x, Int16 y) { return x.value < y; } public static bool operator >(Integer x, Int32 y) { return x.value > y; } public static bool operator <(Integer x, Int32 y) { return x.value < y; } public static bool operator >(Integer x, Int64 y) { return x.value > y; } public static bool operator <(Integer x, Int64 y) { return x.value < y; } public static bool operator >(Integer x, UInt16 y) { return x.value > y; } public static bool operator <(Integer x, UInt16 y) { return x.value < y; } public static bool operator >(Integer x, UInt32 y) { return x.value > y; } public static bool operator <(Integer x, UInt32 y) { return x.value < y; } public static bool operator >(Integer x, UInt64 y) { return x.value > y; } public static bool operator <(Integer x, UInt64 y) { return x.value < y; } public static bool operator >(Integer x, Integer y) { return x.value > y.value; } public static bool operator <(Integer x, Integer y) { return x.value < y.value; } public static bool operator >=(Integer x, Int16 y) { return x.value >= y; } public static bool operator <=(Integer x, Int16 y) { return x.value <= y; } public static bool operator >=(Integer x, Int32 y) { return x.value >= y; } public static bool operator <=(Integer x, Int32 y) { return x.value <= y; } public static bool operator >=(Integer x, Int64 y) { return x.value >= y; } public static bool operator <=(Integer x, Int64 y) { return x.value <= y; } public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; } public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; } public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; } public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; } public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; } public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; } public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; } public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; } public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); } public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); } public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); } public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); } public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); } public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); } public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); } public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); } public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); } public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); } public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); } public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); } public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); } public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); } public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); } public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); } public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); } public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); } public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); } public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); } public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); } public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); } public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); } public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); } public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); } public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); } public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); } public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); } public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); } public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); } public static bool operator ==(Int16 x, Integer y) { return x == y.value; } public static bool operator !=(Int16 x, Integer y) { return x != y.value; } public static bool operator ==(Int32 x, Integer y) { return x == y.value; } public static bool operator !=(Int32 x, Integer y) { return x != y.value; } public static bool operator ==(Int64 x, Integer y) { return x == y.value; } public static bool operator !=(Int64 x, Integer y) { return x != y.value; } public static bool operator ==(UInt16 x, Integer y) { return x == y.value; } public static bool operator !=(UInt16 x, Integer y) { return x != y.value; } public static bool operator ==(UInt32 x, Integer y) { return x == y.value; } public static bool operator !=(UInt32 x, Integer y) { return x != y.value; } public static bool operator ==(UInt64 x, Integer y) { return x == y.value; } public static bool operator !=(UInt64 x, Integer y) { return x != y.value; } public static bool operator >(Int16 x, Integer y) { return x > y.value; } public static bool operator <(Int16 x, Integer y) { return x < y.value; } public static bool operator >(Int32 x, Integer y) { return x > y.value; } public static bool operator <(Int32 x, Integer y) { return x < y.value; } public static bool operator >(Int64 x, Integer y) { return x > y.value; } public static bool operator <(Int64 x, Integer y) { return x < y.value; } public static bool operator >(UInt16 x, Integer y) { return x > y.value; } public static bool operator <(UInt16 x, Integer y) { return x < y.value; } public static bool operator >(UInt32 x, Integer y) { return x > y.value; } public static bool operator <(UInt32 x, Integer y) { return x < y.value; } public static bool operator >(UInt64 x, Integer y) { return x > y.value; } public static bool operator <(UInt64 x, Integer y) { return x < y.value; } public static bool operator >=(Int16 x, Integer y) { return x >= y.value; } public static bool operator <=(Int16 x, Integer y) { return x <= y.value; } public static bool operator >=(Int32 x, Integer y) { return x >= y.value; } public static bool operator <=(Int32 x, Integer y) { return x <= y.value; } public static bool operator >=(Int64 x, Integer y) { return x >= y.value; } public static bool operator <=(Int64 x, Integer y) { return x <= y.value; } public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; } public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; } public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; } public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; } public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; } public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; } } public static class Program { private static bool IntegerFunction(Integer n) { //code that implements IntegerFunction goes here //note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore } private static void Main() { Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known. Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known. Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string" } }

Tenga en cuenta que para usar Dynamic en su código debe agregar referencia a Microsoft.CSharp

Si la versión de .NET framework está por debajo / por debajo / por debajo de 4.0 y dynamic no está definida en esa versión, entonces tendrá que usar el objeto y hacer la conversión al tipo entero, que es un problema, por lo que le recomiendo que lo use menos .NET 4.0 o más reciente si puede, así puede usar dinámico en lugar de objeto .


Solución utilizando políticas:

interface INumericPolicy<T> { T Zero(); T Add(T a, T b); // add more functions here, such as multiplication etc. } struct NumericPolicies: INumericPolicy<int>, INumericPolicy<long> // add more INumericPolicy<> for different numeric types. { int INumericPolicy<int>.Zero() { return 0; } long INumericPolicy<long>.Zero() { return 0; } int INumericPolicy<int>.Add(int a, int b) { return a + b; } long INumericPolicy<long>.Add(long a, long b) { return a + b; } // implement all functions from INumericPolicy<> interfaces. public static NumericPolicies Instance = new NumericPolicies(); }

Algoritmos:

static class Algorithms { public static T Sum<P, T>(this P p, params T[] a) where P: INumericPolicy<T> { var r = p.Zero(); foreach(var i in a) { r = p.Add(r, i); } return r; } }

Uso:

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5); long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5); NumericPolicies.Instance.Sum("www", "") // compile-time error.

La solución es segura en tiempo de compilación. CityLizard Framework proporciona una versión compilada para .NET 4.0. El archivo es lib / NETFramework4.0 / CityLizard.Policy.dll.

También está disponible en Nuget: https://www.nuget.org/packages/CityLizard/ . Ver CityLizard.Policy.I estructura.


Teniendo en cuenta la popularidad de esta pregunta y el interés detrás de tal función, me sorprende ver que todavía no hay una respuesta relacionada con T4.

En este código de ejemplo, demostraré un ejemplo muy simple de cómo puedes usar el potente motor de plantillas para hacer lo que el compilador hace prácticamente entre bastidores con los genéricos.

En lugar de pasar por aros y sacrificar la certeza del tiempo de compilación, simplemente puede generar la función que desee para cada tipo que desee y utilizarla en consecuencia (¡en tiempo de compilación!).

Para hacer esto:

  • Cree un nuevo archivo de plantilla de texto llamado GenericNumberMethodTemplate.tt .
  • Elimine el código generado automáticamente (conservará la mayor parte, pero no es necesario algo).
  • Agregue el siguiente fragmento de código:

<#@ template language="C#" #> <#@ output extension=".cs" #> <#@ assembly name="System.Core" #> <# Type[] types = new[] { typeof(Int16), typeof(Int32), typeof(Int64), typeof(UInt16), typeof(UInt32), typeof(UInt64) }; #> using System; public static class MaxMath { <# foreach (var type in types) { #> public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) { return val1 > val2 ? val1 : val2; } <# } #> }

Eso es. Ya has terminado.

Guardar este archivo lo compilará automáticamente en este archivo fuente:

using System; public static class MaxMath { public static Int16 Max (Int16 val1, Int16 val2) { return val1 > val2 ? val1 : val2; } public static Int32 Max (Int32 val1, Int32 val2) { return val1 > val2 ? val1 : val2; } public static Int64 Max (Int64 val1, Int64 val2) { return val1 > val2 ? val1 : val2; } public static UInt16 Max (UInt16 val1, UInt16 val2) { return val1 > val2 ? val1 : val2; } public static UInt32 Max (UInt32 val1, UInt32 val2) { return val1 > val2 ? val1 : val2; } public static UInt64 Max (UInt64 val1, UInt64 val2) { return val1 > val2 ? val1 : val2; } }

En su método main puede verificar que tiene certeza en el tiempo de compilación:

namespace TTTTTest { class Program { static void Main(string[] args) { long val1 = 5L; long val2 = 10L; Console.WriteLine(MaxMath.Max(val1, val2)); Console.Read(); } } }

Me adelantaré a un comentario: no, esto no es una violación del principio DRY. El principio DRY está ahí para evitar que las personas dupliquen el código en varios lugares que podrían dificultar el mantenimiento de la aplicación.

Este no es el caso aquí: si desea un cambio, simplemente puede cambiar la plantilla (¡una única fuente para toda su generación!) Y listo.

Para usarlo con sus propias definiciones personalizadas, agregue una declaración de espacio de nombres (asegúrese de que sea la misma que definirá su propia implementación) a su código generado y marque la clase como partial . Luego, agregue estas líneas a su archivo de plantilla para que se incluya en la compilación final:

<#@ import namespace="TheNameSpaceYouWillUse" #> <#@ assembly name="$(TargetPath)" #>

Seamos honestos: esto está muy bien.

Descargo de responsabilidad: esta muestra ha sido fuertemente influenciada por Metaprogramming en .NET por Kevin Hazzard y Jason Bock, Manning Publications .


Yo usaría uno genérico que podrías manejar externamente ...

/// <summary> /// Generic object copy of the same type /// </summary> /// <typeparam name="T">The type of object to copy</typeparam> /// <param name="ObjectSource">The source object to copy</param> public T CopyObject<T>(T ObjectSource) { T NewObject = System.Activator.CreateInstance<T>(); foreach (PropertyInfo p in ObjectSource.GetType().GetProperties()) NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null); return NewObject; }


No hay una sola interfaz o clase base que todos hereden (que no es heredada por otras clases), por lo que la respuesta simple es no.

Aunque me pregunto por qué esto es un problema. ¿Qué desea hacer dentro de su clase IntegerFunction que solo se puede hacer con enteros?


Creo que estás malinterpretando los genéricos. Si la operación que está intentando realizar solo es buena para tipos de datos específicos, entonces no está haciendo algo "genérico".

Además, dado que solo desea permitir que la función funcione en tipos de datos int, no debería necesitar una función separada para cada tamaño específico. Simplemente tomando un parámetro en el tipo específico más grande permitirá que el programa actualice automáticamente los tipos de datos más pequeños. (es decir, pasar un Int16 se convertirá automáticamente a Int64 cuando se realice la llamada).

Si está realizando diferentes operaciones en función del tamaño real de int que se está pasando a la función, creo que debería reconsiderar seriamente incluso intentar hacer lo que está haciendo. Si tiene que engañar al idioma, debe pensar un poco más sobre lo que está tratando de lograr en lugar de cómo hacer lo que quiere.

Si falla todo lo demás, se podría usar un parámetro de tipo Objeto y luego tendrá que verificar el tipo de parámetro y tomar las medidas apropiadas o lanzar una excepción.