Pregunta

Digamos que tengo un objeto de datos, pero este objeto puede contener uno o varios tipos de datos.

class Foo
{
    int intFoo;
    double doubleFoo;
    string stringFoo;
}

Ahora, quiero crear un descriptor de acceso.Alguna manera de conseguir este tipo de datos.Obviamente, yo podría crear varios descriptores de acceso:

public int GetIntFoo();
public double GetDoubleFoo();
public string GetStringFoo();

O puede crear múltiples propiedades

public int IntFoo { get; set; }
public double DoubleFoo { get; set; }
public string StringFoo { get; set; }

No quiero que este es un muy buen diseño.Se requiere el código de cliente para estar más preocupado por el tipo de lo que debería ser.Lo que es más, realmente necesito un único valor para esta clase y la anterior permitiría que uno de cada tipo para ser asignados al mismo tiempo.No es buena.

Una opción es el uso de medicamentos Genéricos.

class Foo<T>
{
    public T TheFoo { get; set; }
}

Sin embargo, esto no crea un Foo, crea una Foo<T>.Un tipo diferente para cada uno, así que realmente no puedo usarlos como del mismo tipo.

Yo podría derivar Foo<T> de FooBase, a continuación, tratar a todos como FooBase, pero luego estoy de vuelta en el problema de acceso a los datos.

Diferentes medicamentos Genéricos opción es usar algo como esto:

class Foo
{
    string stringRepresentationOfFoo;
    public T GetFoo<T>() { return /* code to convert string to type */ }
}

Por supuesto, el problema es que cualquier tipo de T podría ser superado, y, francamente, es un poco ocupado.

Yo también podría simplemente cuadro de los valores y devuelven un objeto, pero luego no hay ningún tipo de seguridad.

Idealmente, quiero tratar a todos los Foo es el mismo, pero quiero que tipo de seguridad, de modo que si no hay un StringFoo, ni siquiera puedo compilar una referencia a un StringFoo.

Foo foo = new Foo("Foo");
string sFoo = foo.Value;  // succeeds.
&nbsp;
Foo foo = new Foo(0);
int iFoo = foo.Value; // succeeds
string sFoo = foo.Value; // compile error

Tal vez esto no es posible..y voy a tener que hacer algunas concesiones, pero tal vez me estoy perdiendo algo.

Alguna idea?

EDITAR:

Ok, así como daniel señala, el tiempo de compilación de la comprobación de un tipo en tiempo de ejecución no es práctico.

¿Cuál es mi mejor opción para hacer lo que yo quiero hacer aquí?Es decir, Tratar a todos los Foo es el mismo, pero todavía relativamente cuerdo mecanismo de acceso?

EDIT2:

No quiero convertir el valor a diferentes tipos.Quiero devolver el tipo correcto para el valor.Es decir, si es un doble, no quiero devolver un int.

¿Fue útil?

Solución

¿Qué hay de pasar la variable como parámetro al get? Así:

int i = foo.get(i);

Luego, en tu clase, tendrías algo como:

public int get(int p) {
    if(this.type != INTEGER) throw new RuntimeException("Data type mismatch");
    return this.intVal;
}

public float get(float p) {
    if(this.type != FLOAT) throw new RuntimeException("Data type mismatch");
    return this.floatVal;
}

Esto hace que el tipo de verificación sea de adentro hacia afuera: en lugar de verificar qué tipo tiene foo, tienes que verificar qué tipo quieres. Si puede darle ese tipo, lo hace, o de lo contrario arroja una excepción de tiempo de ejecución.

Otros consejos

No creo que esto pueda funcionar (dándole el error del compilador que desea)

¿Qué le gustaría que hiciera esto?

Foo bar = (new Random()).Next(2) == 0 ? new Foo("bar") : new Foo(1);
int baz = bar.Value;

¿Es eso un error del compilador?

Creo que " trátalos a todos por igual " (al menos de la manera en que lo ha descrito) y " error de tiempo de compilación " van a ser mutuamente excluyentes.

En cualquier caso, creo que " la mejor manera " va a ser un compromiso entre genéricos y herencia. Puede definir un Foo<T> que sea una subclase de Foo; entonces aún puede tener colecciones de Foo.

abstract public class Foo
{
  // Common implementation

  abstract public object ObjectValue { get; }
}

public class Foo<T> : Foo
{
   public Foo(T initialValue)
   {
      Value = initialValue;
   }

   public T Value { get; set; }

   public object ObjectValue
   { 
      get { return Value; }
   }
}

Muchos sistemas utilizan métodos auxiliares para devolver los tipos alternativos, tal como el objeto base de .NET Framework tiene el método ToString ()

Elija cuál es el mejor tipo base para cada uno de sus objetos y proporcione métodos To para otros casos

por ejemplo

class Foo{
    public Int32 Value { get; set; }
    public Byte ToByte() { return Convert.ToByte(Value); }
    public Double ToDouble() { return (Double)Value; }
    public new String ToString() { return Value.ToString("#,###"); }
}

Una cosa es almacenar cualquier tipo en su estado interno de la clase, y otra es exponerlo externamente. Cuando escribes una clase, en realidad estás declarando un contrato por su comportamiento. La forma en que lo escriba influirá en gran medida en el aspecto del código del cliente al usar la clase.

Por ejemplo, implementando la interfaz IConvertible usted indique que su tipo se puede convertir a cualquier tipo CLR como un valor equivalente.

También he visto implementaciones en las que se usaba una clase Value para almacenar resultados de cálculos, resultados que podrían representar una cadena, doble, int o booleana. Pero, el problema era que el código del cliente tenía que verificar una propiedad Value.Type de una enumeración {String, Integer, Double, Boolean} y luego emitir la propiedad Value.Value (que fue expuesta externamente por la clase Value como un tipo de objeto ) o use los captadores ValueString, ValueDouble, ValueInt, ValueBoolean específicos.

Por qué no usar simplemente string, double y int?


Después de info acerca de la colección:¿Qué acerca del uso object?Usted tendrá que comprobar los tipos y tal después de todos modos.Y para ayudarte con la que puede utilizar el is y as a los operadores.Y el Enumerable.Método De Reparto, o mejor aún, la Enumerable.OfType Método.

En realidad, ¿cuál es el propósito de esta clase? El mayor problema parece ser el diseño que rompe al menos SRP (principio de responsabilidad única).

No obstante, si lo estoy leyendo correctamente, le gustaría almacenar algún valor en el contenedor, pasar el contenedor al cliente y recuperar de forma segura el valor.

Con este enfoque, puede utilizar su propuesta, es decir,

namespace Project1 {
public class Class1 {
    static int Main(string[] args) {
        Foo a = new Foo();
        a.SetValue(4);
        Console.WriteLine(a.GetValue<int>());

        Foo b = new Foo();
        a.SetValue("String");
        Console.WriteLine(a.GetValue<string>());

        Console.ReadLine();

        return 0;
    }
}


class Foo {
    private object value; // watch out for boxing here!
    public void SetValue(object value) {
        this.value = value;
    }

    public T GetValue<T>() {
        object val = this.value;
        if (val == null) { return default(T); } // or throw if you prefer
        try {
            return (T)val;
        }
        catch (Exception) {
            return default(T);
            // cast failed, return default(T) or throw
        }
    }
}
}

Sin embargo, en ese caso, ¿por qué no simplemente pasar los datos como objeto y emitirlos usted mismo?

Dependiendo de sus necesidades, también puede probar " PHP en C # " ;:

namespace Project1 {
public class Class1 {
    static int Main(string[] args) {
        MyInt a = 1;
        MyInt b = "2";

        Console.WriteLine(a + b); // writes 3

        Console.ReadLine();

        return 0;
    }
}


class MyInt {
    private int value;

    public static implicit operator int(MyInt container) {
        return container.value;
    }

    public static implicit operator MyInt(int value) {
        MyInt myInt = new MyInt();
        myInt.value = value;
        return myInt ;
    }

    public static implicit operator MyInt(string stringedInt) {
        MyInt myInt = new MyInt();
        myInt.value = int.Parse(stringedInt);
        return myInt;
    }
}
}

Lo siento, simplemente no compro tu premisa. Si todos los datos tienen el mismo propósito, todos deberían tener el mismo tipo. Considere una clase destinada a mantener la temperatura actual, tal como la devuelve uno de varios servicios web. Todos los servicios devuelven la temperatura en centígrados. Pero uno regresa como int, uno regresa como doble y uno lo devuelve como una cadena.

No son tres tipos diferentes, es un tipo, doble. Simplemente necesitaría convertir los retornos no dobles en dobles, que es la temperatura (o quizás flotante).

En general, si tiene múltiples representaciones de una cosa, entonces sigue siendo una cosa, no múltiples. Convierta las representaciones múltiples en una.

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