Comprobación de tipo genérico
-
08-06-2019 - |
Pregunta
¿Hay alguna manera de hacer cumplir/limitar los tipos que se pasan a las primitivas? (bool, int, cadena, etc.)
Ahora, sé que puedes limitar el parámetro de tipo genérico a una implementación de tipo o interfaz a través del dónde cláusula.Sin embargo, esto no se ajusta a los requisitos de los primitivos (AFAIK) porque no todos tienen un terreno común (aparte de objeto antes de que alguien diga!:PAG).
Entonces, mis pensamientos actuales son simplemente apretar los dientes y hacer un gran cambiar declaración y lanzar un ArgumentoExcepción en el fracaso..
EDITAR 1:
Solo para aclarar:
La definición del código debería ser como:
public class MyClass<GenericType> ....
Y creación de instancias:
MyClass<bool> = new MyClass<bool>(); // Legal
MyClass<string> = new MyClass<string>(); // Legal
MyClass<DataSet> = new MyClass<DataSet>(); // Illegal
MyClass<RobsFunkyHat> = new MyClass<RobsFunkyHat>(); // Illegal (but looks awesome!)
EDITAR 2
@Jon Limjap: buen punto y algo que ya estaba considerando.Estoy seguro de que existe un método genérico que se puede utilizar para determinar si el tipo es de valor o de referencia.
Esto podría ser útil para eliminar instantáneamente muchos de los objetos con los que no quiero tratar (pero luego debes preocuparte por las estructuras que se utilizan, como Tamaño )..Interesante problema, ¿no?:)
Aquí lo tienes:
where T : struct
Tomado de MSDN.
Soy curioso..¿Podría hacerse esto en .NET 3.x usando métodos de extensión?Cree una interfaz e implemente la interfaz en los métodos de extensión (lo que probablemente sería más limpio que un interruptor un poco gordo).Además, si luego necesita ampliar a cualquier tipo personalizado ligero, también pueden implementar la misma interfaz, sin necesidad de cambios en el código base.
¿Qué piensan ustedes?
¡¡La triste noticia es que estoy trabajando en Framework 2!!:D
EDITAR 3
Esto fue tan simple siguiendo desde Puntero de Jon Limjaps..Tan simple que casi quiero llorar, ¡pero es genial porque el código funciona de maravilla!
Así que esto es lo que hice (¡te reirás!):
Código agregado a la clase genérica.
bool TypeValid()
{
// Get the TypeCode from the Primitive Type
TypeCode code = Type.GetTypeCode(typeof(PrimitiveDataType));
// All of the TypeCode Enumeration refer Primitive Types
// with the exception of Object and Empty (Null).
// Since I am willing to allow Null Types (at this time)
// all we need to check for is Object!
switch (code)
{
case TypeCode.Object:
return false;
default:
return true;
}
}
Luego, un pequeño método de utilidad para verificar el tipo y generar una excepción,
private void EnforcePrimitiveType()
{
if (!TypeValid())
throw new InvalidOperationException(
"Unable to Instantiate SimpleMetadata based on the Generic Type of '" + typeof(PrimitiveDataType).Name +
"' - this Class is Designed to Work with Primitive Data Types Only.");
}
Lo único que hay que hacer entonces es llamar Aplicar tipo primitivo() en las clases constructores.¡Trabajo hecho!:-)
El único inconveniente es que solo arroja una excepción en tiempo de ejecución (obviamente) en lugar de en tiempo de diseño.Pero eso no es gran cosa y podría solucionarse con utilidades como FxCop (que no usamos en el trabajo).
¡Un agradecimiento especial a Jon Limjap por este!
Solución
Las primitivas parecen estar especificadas en el TypeCode
enumeración:
Quizás haya una manera de saber si un objeto contiene el TypeCode enum
sin tener que lanzarlo a un objeto o llamada específica GetType()
o typeof()
?
Actualizar Estaba justo debajo de mis narices.El ejemplo de código allí muestra esto:
static void WriteObjectInfo(object testObject)
{
TypeCode typeCode = Type.GetTypeCode( testObject.GetType() );
switch( typeCode )
{
case TypeCode.Boolean:
Console.WriteLine("Boolean: {0}", testObject);
break;
case TypeCode.Double:
Console.WriteLine("Double: {0}", testObject);
break;
default:
Console.WriteLine("{0}: {1}", typeCode.ToString(), testObject);
break;
}
}
}
Sigue siendo un cambio feo.¡Pero es un buen punto de partida!
Otros consejos
public class Class1<GenericType> where GenericType : struct
{
}
Este pareció hacer el trabajo...
Más o menos lo que @Lars ya dijo:
//Force T to be a value (primitive) type.
public class Class1<T> where T: struct
//Force T to be a reference type.
public class Class1<T> where T: class
//Force T to be a parameterless constructor.
public class Class1<T> where T: new()
Todos funcionan en .NET 2, 3 y 3.5.
Si puedes tolerar el uso de métodos de fábrica (en lugar de los constructores MyClass que solicitaste), siempre puedes hacer algo como esto:
class MyClass<T>
{
private readonly T _value;
private MyClass(T value) { _value = value; }
public static MyClass<int> FromInt32(int value) { return new MyClass<int>(value); }
public static MyClass<string> FromString(string value) { return new MyClass<string>(value); }
// etc for all the primitive types, or whatever other fixed set of types you are concerned about
}
Un problema aquí es que necesitarías escribir MyClass<AnyTypeItDoesntMatter>.FromInt32
, lo cual es molesto.No hay una muy buena manera de solucionar esto si desea mantener la privacidad del constructor, pero aquí hay un par de soluciones:
- Crear una clase abstracta
MyClass
.HacerMyClass<T>
heredar deMyClass
y anidarlo dentroMyClass
.Mueva los métodos estáticos aMyClass
.Así se arreglará toda la visibilidad, a costa de tener que accederMyClass<T>
comoMyClass.MyClass<T>
. - Usar
MyClass<T>
como se indica.Hacer una clase estáticaMyClass
que llama a los métodos estáticos enMyClass<T>
usandoMyClass<AnyTypeItDoesntMatter>
(probablemente usando el tipo apropiado cada vez, solo para reír). - (Más fácil, pero ciertamente extraño) Haz un tipo abstracto
MyClass
que hereda deMyClass<AnyTypeItDoesntMatter>
.(Para ser más concretos, digamosMyClass<int>
.) Debido a que puede llamar a métodos estáticos definidos en una clase base a través del nombre de una clase derivada, ahora puede usarMyClass.FromString
.
Esto le brinda verificación estática a expensas de más escritura.
Si está satisfecho con la verificación dinámica, usaría alguna variación de la solución TypeCode anterior.
@Robar, Enum
se deslizará a través del TypeValid
funcionar como es TypeCode
es Integer
.He actualizado la función para verificar también Enum
.
Private Function TypeValid() As Boolean
Dim g As Type = GetType(T)
Dim code As TypeCode = Type.GetTypeCode(g)
' All of the TypeCode Enumeration refer Primitive Types
' with the exception of Object and Empty (Nothing).
' Note: must also catch Enum as its type is Integer.
Select Case code
Case TypeCode.Object
Return False
Case Else
' Enum's TypeCode is Integer, so check BaseType
If g.BaseType Is GetType(System.Enum) Then
Return False
Else
Return True
End If
End Select
End Function
Puedes simplificar el EnforcePrimitiveType
método usando typeof(PrimitiveDataType).IsPrimitive
propiedad.¿Me estoy perdiendo de algo?
Utilice una costumbre FxCop regla que señala el uso indeseable de MyClass<>
.
Al tener un desafío similar, me preguntaba cómo se sintieron ustedes acerca del Interfaz convertible.Permite lo que el solicitante requiere y puede ampliarlo con sus propias implementaciones.
Ejemplo:
public class MyClass<TKey>
where TKey : IConvertible
{
// class intentionally abbreviated
}
Estoy pensando en esto como una solución, aunque muchas de las sugeridas también formaban parte de mi selección.
Sin embargo, mi preocupación es: ¿es engañoso para los posibles desarrolladores que utilizan su clase?
Saludos y gracias.