質問

データオブジェクトがあるとしますが、このオブジェクトはいくつかのタイプのデータの1つを保持できます。

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

次に、アクセサーを作成します。このデータを取得する方法。明らかに、複数のアクセサーを作成できます:

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

または、複数のプロパティを作成できます

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

これは非常に良いデザインではありません。それは、クライアントコードが必要以上にタイプを心配することを要求します。さらに、このクラスには実際に単一の値のみが必要であり、上記では各タイプの1つを同時に割り当てることができます。良くない。

1つのオプションは、ジェネリックを使用することです。

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

ただし、これはFooを作成するのではなく、Foo <!> lt; T <!> gt;を作成します。それぞれ異なるタイプなので、同じタイプとして実際に使用することはできません。

Foo <!> lt; T <!> gt;を導出できました。 FooBaseから、それらすべてをFooBaseとして扱いますが、データにアクセスする問題に戻ります。

別のジェネリックオプションは、次のようなものを使用することです:

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

もちろん、問題はどんな種類のTでも渡すことができるということであり、率直に言って、それは少し忙しいです。

値をボックス化してオブジェクトを返すこともできますが、タイプセーフはありません。

理想的には、すべてのFooを同じように扱いたいのですが、StringFooがない場合は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

これはおそらく不可能です。そして、私はいくつかの妥協をしなければなりませんが、多分私は何かを見逃しています。

アイデアはありますか

編集:

OK、ダニエルが指摘するように、ランタイム型のコンパイル時のチェックは実用的ではありません。

ここでやりたいことをするための最良の選択肢は何ですか?すなわち、すべてのFooを同じように扱いますが、それでも比較的健全なアクセスメカニズムを持っていますか?

EDIT2:

値を異なる型に変換したくありません。値の正しい型を返したい。つまり、それがdoubleの場合、intを返したくありません。

役に立ちましたか?

解決

getのパラメーターとして変数を渡すのはどうですか?このように:

int i = foo.get(i);

その後、クラスでは次のようになります:

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

この種のタイプチェックは裏返しになります:fooがどのタイプを保持するかをチェックする代わりに、fooが必要なタイプをチェックします。そのタイプを提供できる場合は提供し、そうでない場合はランタイム例外をスローします。

他のヒント

これでうまくいくとは思わない(必要なコンパイラエラーが表示される)

これで何をしたい:

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

それはコンパイラエラーですか?

<!> quot;すべて同じように扱う<!> quot; (少なくとも説明したとおり)および<!> quot; compile time error <!> quot;相互に排他的になります。

とにかく、<!> quot;最善の方法<!> quot;ジェネリックと継承の間の妥協点になります。 FooのサブクラスであるFoo<T>を定義できます。引き続き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; }
   }
}

.netフレームワークのベースオブジェクトにToString()メソッドがあるように、多くのシステムはヘルパーメソッドを使用して代替タイプを返します

各オブジェクトに最適な基本型を選択し、他の場合にToメソッドを提供します

e.g。

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

1つは、クラスの内部状態に任意の型を格納することであり、もう1つは、外部に公開することです。クラスを記述するとき、実際にはその動作のコントラクトを宣言しています。記述方法は、クラスを使用するときのクライアントコードの外観に大きく影響します。

たとえば、 IConvertible インターフェースを実装することにより、型を同等の値として任意のCLR型に変換できると述べています。

また、Valueクラスを使用して、文字列、double、int、booleanのいずれかを表す計算結果を保存する実装を見てきました。しかし、問題は、クライアントコードが列挙型{String、Integer、Double、Boolean}のValue.Typeプロパティを確認し、Value.Valueプロパティ(Valueクラスによってオブジェクト型として外部に公開された)をキャストする必要があることでした)または特定のValueString、ValueDouble、ValueInt、ValueBooleanゲッターを使用します。

なぜstringdoubleおよびintを使用しないのですか?


コレクションに関する情報の後:objectの使用についてはどうですか?いずれにせよ、後でタイプなどを確認する必要があります。それに役立つように、isおよびas演算子を使用できます。また、 Enumerable.Castメソッド、またはさらに優れた Enumerable.OfTypeメソッド

実際、このクラスの目的は何ですか?最大の問題は、少なくともSRP(単一責任原則)を破る設計のようです。

それにもかかわらず、私がそれを正しく読んでいる場合、コンテナに値を保存し、コンテナをクライアントに渡し、値をタイプセーフに取得したいです。

このアプローチでは、提案を使用できます。つまり、

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

ただし、その場合、データをオブジェクトとして渡し、自分でキャストしないのはなぜですか?

ニーズに応じて、<!> quot; PHP in C#<!> quot;:

を試すこともできます。
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;
    }
}
}

申し訳ありませんが、あなたの施設を購入していません。データの目的がすべて同じである場合、それらはすべて同じタイプである必要があります。いくつかのWebサービスの1つによって返される、現在の温度を保持するためのクラスを考えてください。すべてのサービスは摂氏で温度を返します。ただし、intとして返され、doubleとして返され、文字列として返されます。

3つの異なるタイプではなく、1つのタイプ-ダブルです。単純に、double以外の戻り値をdoubleに変換する必要があります。これが温度です(またはフロート)。

一般に、1つのものの複数の表現がある場合、それはまだ1つのものであり、複数のものではありません。複数の表現を1つに変換します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top