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!

¿Fue útil?

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.Hacer MyClass<T> heredar de MyClass y anidarlo dentro MyClass.Mueva los métodos estáticos a MyClass.Así se arreglará toda la visibilidad, a costa de tener que acceder MyClass<T> como MyClass.MyClass<T>.
  • Usar MyClass<T> como se indica.Hacer una clase estática MyClass que llama a los métodos estáticos en MyClass<T> usando MyClass<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 de MyClass<AnyTypeItDoesntMatter>.(Para ser más concretos, digamos MyClass<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 usar MyClass.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, Enumse 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top