Question

Supposons que j'ai un objet de données, mais que cet objet peut contenir l'un des types de données.

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

Maintenant, je veux créer un accesseur. Un moyen d'obtenir ces données. Évidemment, je pourrais créer plusieurs accesseurs:

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

Ou je pourrais créer plusieurs propriétés

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

Je ne pense pas que ce soit un très bon design. Cela nécessite que le code client soit plus préoccupé par le type qu'il ne devrait l'être. De plus, je n'ai vraiment besoin que d'une seule valeur pour cette classe et ce qui précède permettrait d'affecter simultanément un type de chaque type. Pas bien.

Une option consiste à utiliser des génériques.

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

Cependant, cela ne crée pas un Foo, il crée un Foo < T > ;. Un type différent pour chacun, donc je ne peux pas vraiment les utiliser comme le même type.

Je pourrais obtenir Foo < T > de FooBase, puis traitez-les tous comme ceux de FooBase, mais je suis de retour dans le problème de l'accès aux données.

Une autre option pour les génériques consiste à utiliser quelque chose comme ceci:

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

Bien sûr, le problème est que tout type de T peut être adopté, et franchement, il est un peu occupé.

Je pourrais aussi simplement encadrer les valeurs et renvoyer un objet, mais il n'y a alors aucun type de sécurité.

Idéalement, je veux traiter tous les Foo de la même manière, mais je veux une sécurité de type afin que, s’il n’existe pas de StringFoo, je ne puisse même pas compiler de référence à 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

Peut-être que ce n'est même pas possible… et je devrai faire des compromis, mais peut-être qu'il me manque quelque chose.

Des idées?

EDIT:

Ok, comme le fait remarquer Daniel, la vérification de la compilation d’un type d’exécution n’est pas pratique.

Quelle est ma meilleure option pour faire ce que je veux faire ici? À savoir, traiter tous les Foo de la même manière, mais toujours avoir un mécanisme d'accès relativement sain d'esprit?

EDIT2:

Je ne veux pas convertir la valeur en différents types. Je veux renvoyer le type correct pour la valeur. C’est-à-dire que s’il s’agit d’un double, je ne veux pas retourner un int.

Était-ce utile?

La solution

Pourquoi ne pas transmettre la variable en tant que paramètre à get? Comme ceci:

int i = foo.get(i);

Ensuite, dans votre classe, vous auriez quelque chose comme:

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

Cette sorte de tourne la vérification de type à l'envers: au lieu de vérifier quel type est foo, vous devez vérifier quel type vous voulez. S'il peut vous donner ce type, c'est le cas, sinon il lève une exception d'exécution.

Autres conseils

Je ne pense pas que cela pourrait fonctionner (en vous donnant l'erreur du compilateur que vous voulez)

Que voudriez-vous que cela fasse:

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

Est-ce une erreur de compilation?

Je pense & "traitez-les tous de la même manière &"; (au moins la façon dont vous l'avez décrit) et & "Erreur de compilation &"; vont être mutuellement exclusifs.

Dans tous les cas, je pense que la & "meilleure façon &"; va être un compromis entre les génériques et l'héritage. Vous pouvez définir un Foo<T> qui est une sous-classe de Foo; alors vous pouvez toujours avoir des collections 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; }
   }
}

De nombreux systèmes utilisent des méthodes d'assistance pour renvoyer les autres types, tout comme l'objet de base des frameworks .net a la méthode ToString ()

Choisissez quel est le meilleur type de base pour chacun de vos objets et fournissez les méthodes To pour les autres cas

par exemple

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

Une chose est de stocker n'importe quel type dans votre état interne de la classe, et une autre consiste à l'exposer de manière externe. Lorsque vous écrivez une classe, vous déclarez en réalité un contrat pour son comportement. La manière dont vous l'écrivez aura une grande influence sur l'apparence du code client lors de l'utilisation de la classe.

Par exemple, en implémentant l'interface IConvertible Indiquez que votre type peut être converti en n'importe quel type CLR en tant que valeur équivalente.

J'ai également vu des implémentations dans lesquelles une classe Value était utilisée pour stocker les résultats des calculs, des résultats pouvant représenter une chaîne, double, int ou boolean. Mais le problème était que le code client devait vérifier une propriété Value.Type d'une enum {String, Integer, Double, Boolean}, puis convertir la propriété Value.Value (exposée en externe par la classe Value en tant que type Object ) ou utilisez les getters spécifiques ValueString, ValueDouble, ValueInt, ValueBoolean.

Pourquoi ne pas simplement utiliser string, double et int?

Après les informations sur la collecte: qu’en est-il de l’utilisation de object? De toute façon, vous devrez vérifier les types et autres. Et pour vous aider, vous pouvez utiliser les opérateurs is et as. Et la méthode Enumerable.Cast , ou mieux encore, la Méthode Enumerable.OfType .

En fait, quel est le but de cette classe? Le plus gros problème semble être la rupture de conception au moins SRP (principe de responsabilité unique).

Néanmoins, si je le lis correctement, vous souhaitez stocker une valeur dans le conteneur, le transmettre au client et récupérer la valeur en toute sécurité.

Avec cette approche, vous pouvez utiliser votre proposition, c'est-à-dire.

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

Cependant, dans ce cas, pourquoi ne pas simplement transmettre des données en tant qu'objet et les diffuser vous-même?

Selon vos besoins, vous pouvez aussi essayer " 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;
    }
}
}

Je suis désolé, je n'achète tout simplement pas votre prémisse. Si les données ont toutes le même objectif, elles doivent toutes avoir le même type. Prenons une classe censée contenir la température actuelle, telle que renvoyée par l'un des nombreux services Web. Tous les services renvoient la température en Celsius. Mais on retourne comme un int, on retourne comme un double et on le retourne comme une chaîne.

Ce ne sont pas trois types différents - c’est un type - double. Vous auriez simplement besoin de convertir les retours non-doubles en doubles, ce qui correspond à la température (ou peut-être un flottement).

En général, si vous avez plusieurs représentations d’une même chose, c’est toujours une seule chose, pas plusieurs. Convertissez les représentations multiples en une seule.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top