Pergunta

Digamos que eu tenha um objeto de dados, mas esse objeto pode conter um dos vários tipos de dados.

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

Agora, quero criar um acessador. Alguma maneira de obter esses dados. Obviamente, eu poderia criar vários acessores:

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

Ou eu poderia criar várias propriedades

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

Eu não, isso é um design muito bom. Requer que o código do cliente esteja mais preocupado com o tipo do que deveria ser. Além disso, eu realmente preciso de apenas um valor único para esta classe e o acima permitiria que um de cada tipo fosse atribuído ao mesmo tempo. Não é bom.

Uma opção é usar genéricos.

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

No entanto, isso não cria um foo, cria um foou003CT> . Um tipo diferente para cada um, então não posso usá -los como o mesmo tipo.

Eu poderia derivar foou003CT> Do FooBase, trate todos eles como foobase, mas depois estou de volta ao problema de acessar os dados.

Uma opção genérica diferente é usar algo assim:

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

É claro que o problema é que qualquer tipo de T pode ser aprovado e, francamente, está um pouco ocupado.

Eu também poderia apenas encaixar os valores e retornar um objeto, mas não há segurança de tipo.

Idealmente, quero tratar todos os iguais, mas quero a segurança do tipo para que, se não houver um StringFoo, não consigo nem compilar uma referência a um 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

Talvez isso não seja possível ... e terei que fazer alguns compromissos, mas talvez eu esteja perdendo alguma coisa.

Alguma ideia?

EDITAR:

Ok, então, como Daniel aponta, a verificação de tempo de compilação de um tipo de tempo de execução não é prática.

Qual é a minha melhor opção para fazer o que eu quero fazer aqui? Ou seja, tratar todos os foo é o mesmo, mas ainda tem um mecanismo de acesso relativamente sã?

Edit2:

Não quero converter o valor em diferentes tipos. Eu quero devolver o tipo correto para o valor. Ou seja, se for um dobro, não quero devolver um int.

Foi útil?

Solução

Que tal passar na variável como um parâmetro para o GET? Assim:

int i = foo.get(i);

Então, na sua classe, você teria 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;
}

Esse tipo de gira o tipo de verificação de dentro para fora: em vez de verificar o tipo que o Foo segura, você verifica que tipo você deseja. Se puder fornecer esse tipo, ele faz uma exceção de tempo de execução.

Outras dicas

Eu não acho que isso possa funcionar (dando a você o erro do compilador que você deseja)

O que você gostaria que isso fizesse:

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

Isso é um erro do compilador?

Eu acho que "trate -os da mesma forma" (pelo menos da maneira que você descreveu) e "Erro de tempo de compilação" serão mutuamente exclusivos.

De qualquer forma, acho que a "melhor maneira" será um compromisso entre genéricos e herança. Você pode definir um Foo<T> Isso é uma subclasse de Foo; Então você ainda pode ter coleções 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; }
   }
}

Muitos sistemas usam métodos auxiliares para retornar os tipos alternativos, assim como o objeto base .NET Frameworks possui o método ToString ()

Escolha qual é o melhor tipo de base para cada seu objeto e forneça métodos para outros casos

por exemplo

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("#,###"); }
}

Uma coisa é armazenar qualquer tipo no seu estado interno da classe, e outra é expô -la externamente. Quando você escreve uma aula, na verdade você está declarando um contrato para seu comportamento. A maneira como você o escreve influenciará muito a aparência do código do cliente ao usar a classe.

Por exemplo, implementando o Iconvertible Interface Você afirma que seu tipo pode ser convertido em qualquer tipo CLR como um valor equivalente.

Também vi implementações em que uma classe de valor foi usada para armazenar resultados de cálculos, resultados que poderiam representar uma string, dupla, int ou booleana. Porém, o problema era que o código do cliente tinha que verificar uma propriedade Value.Type de uma enum {String, Integer, Double, Boolean} e depois lançar a propriedade Value.Value (que foi exposta externamente pela classe Value como um tipo de objeto ) ou use os getters específicos de avaliação, valorizando, valueInt, ValueBoolean.

Por que não apenas usar string, double e int?


Depois das informações sobre a coleção: que tal usar object? Você terá que verificar se há tipos e outros depois. E para ajudá -lo com isso, você pode usar o is e as operadores. E a Método enumerável.cast, ou melhor ainda, o Enumerable.Oftype Método.

Na verdade, qual é o objetivo desta classe? O maior problema parece ser o design da quebra de pelo menos SRP (princípio de responsabilidade única).

No entanto, se eu estiver lendo corretamente, você gostaria de armazenar algum valor no contêiner, passe o contêiner para o cliente e recupere-se do tipo.

Com esta abordagem, você pode usar sua proposta, ou seja,

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
        }
    }
}
}

No entanto, nesse caso, por que não simplesmente passar dados como objeto e lançar por você mesmo?

Dependendo das suas necessidades, você também pode tentar "PHP em 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;
    }
}
}

Sinto muito, eu simplesmente não compro sua premissa. Se todos os dados tiverem o mesmo objetivo, todos devem ter o mesmo tipo. Considere uma classe destinada a manter a temperatura atual, conforme retornado por um dos vários serviços da Web. Todos os serviços retornam a temperatura em centígrados. Mas alguém retorna como um INT, retorna como um duplo e o retorna como uma string.

Não são três tipos diferentes - é um tipo - o dobro. Você simplesmente precisaria converter os retornos não duplos em dobro, que é a temperatura (ou talvez flutuar).

Em geral, se você tem várias representações de uma coisa, ainda é uma coisa, não várias coisas. Converter as múltiplas representações em uma.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top