16

指定された型 T の値を保存するジェネリック クラスがあります。値は、int、uint、double、または float にすることができます。ここで、値のバイトを取得して、特定のプロトコルにエンコードしたいと考えています。したがって、メソッド BitConverter.GetBytes() を使用したいのですが、残念ながら Bitconverter はジェネリック型または未定義のオブジェクトをサポートしていません。そのため、値をキャストして GetBytes() の特定のオーバーロードを呼び出します。私の質問: 汎用値を int、double、または float にキャストするにはどうすればよいですか? これは機能しません:

public class GenericClass<T>
    where T : struct
{
    T _value;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public byte[] GetBytes()
    {
        //int x = (int)this._value;
        if(typeof(T) == typeof(int))
        {
            return BitConverter.GetBytes((int)this._value);
        }
        else if (typeof(T) == typeof(double))
        {
            return BitConverter.GetBytes((double)this._value);
        }
        else if (typeof(T) == typeof(float))
        {
            return BitConverter.GetBytes((float)this._value);
        }
    }
}

一般的な値をキャストする可能性はありますか? または、バイトを取得する別の方法はありますか?

4

8 に答える 8

28

まず、これは本当に悪いコードの匂いです。このように型パラメーターに対して型テストを行っているときはいつでも、ジェネリックを悪用している可能性が高いです。

C# コンパイラは、このようにジェネリックを悪用していることを認識し、T 型の値から int などへのキャストを許可しません。値を int にキャストする前に object にキャストすることで、邪魔になるコンパイラを無効にすることができます。

return BitConverter.GetBytes((int)(object)this._value);

うん。繰り返しますが、これを行う別の方法を見つけたほうがよいでしょう。例えば:

public class NumericValue
{
    double value;
    enum SerializationType { Int, UInt, Double, Float };
    SerializationType serializationType;        

    public void SetValue(int value)
    {
        this.value = value;
        this.serializationType = SerializationType.Int
    }
    ... etc ...

    public byte[] GetBytes()
    {
        switch(this.serializationType)
        {
            case SerializationType.Int:
                return BitConverter.GetBytes((int)this.value);
            ... etc ...

ジェネリックは必要ありません。実際にジェネリックである状況のためにジェネリックを予約します。型の種類ごとにコードを4 回書いたとしても、ジェネリックからは何も得られません。

于 2013-04-11T21:11:38.360 に答える
11

そもそも、その型が適切にジェネリックになっていないことに気がつきました。数少ない型のうちの 1 つにすぎず、その制約を表現することはできません。

GetBytes次に、 の型に基づいての別のオーバーロードを呼び出しますT。ジェネリックは、そのようなものにはうまく機能しません。.NET 4 以降では、動的型付けを使用してそれを実現できます。

public byte[] GetBytes()
{
    return BitConverter.GetBytes((dynamic) _value);
}

...しかし、これもあまりいいデザインとは思えません。

于 2013-04-11T21:09:30.970 に答える
7

かなり遅い答えですが、とにかく...少し良くする方法があります...この方法でジェネリックを利用します:型を変換する別のジェネリック型を実装します。したがって、ボックス化解除、タイプのオブジェクトへのキャストなどを気にする必要はありません...それはうまくいきます。

また、GenericClass では、タイプを切り替える必要がなくなりました。使用IValueConverter<T>してキャストすることも できますas IValueConverter<T>。このように、ジェネリックは正しいインターフェイスの実装を見つけるための魔法を行い、さらに、 T がサポートされていないものである場合、オブジェクトは null になります...

interface IValueConverter<T> where T : struct
{
    byte[] FromValue(T value);
}

class ValueConverter:
    IValueConverter<int>,
    IValueConverter<double>,
    IValueConverter<float>
{
    byte[] IValueConverter<int>.FromValue(int value)
    {
        return BitConverter.GetBytes(value);
    }

    byte[] IValueConverter<double>.FromValue(double value)
    {
        return BitConverter.GetBytes(value);
    }

    byte[] IValueConverter<float>.FromValue(float value)
    {
        return BitConverter.GetBytes(value);
    }
}

public class GenericClass<T> where T : struct
{
    T _value;

    IValueConverter<T> converter = new ValueConverter() as IValueConverter<T>;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public byte[] GetBytes()
    {
        if (converter == null)
        {
            throw new InvalidOperationException("Unsuported type");
        }

        return converter.FromValue(this._value);
    }
}
于 2015-03-18T22:55:35.683 に答える
3

Convert.ToInt32(this._value)またはを潜在的に使用できます(int)((object)this._value)。しかし一般に、ジェネリック メソッドで特定の型をチェックする必要がある場合は、設計に問題があります。

あなたの場合、抽象基本クラスを作成してから、使用する型の派生クラスを作成することを検討する必要があります。

public abstract class GenericClass<T>
where T : struct
{
    protected T _value;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public abstract byte[] GetBytes();
}

public class IntGenericClass: GenericClass<int>
{
    public override byte[] GetBytes()
    {
        return BitConverter.GetBytes(this._value);
    }
}
于 2013-04-11T21:12:18.967 に答える
3

GetBytes メソッドをこれらの型に追加することが唯一の目標である場合、次のように拡張メソッドとして追加する方がはるかに優れたソリューションではないでしょうか。

public static class MyExtensions {
    public static byte[] GetBytes(this int value) {
        return BitConverter.GetBytes(value) ;
    }
    public static byte[] GetBytes(this uint value) {
        return BitConverter.GetBytes(value) ;
    }
    public static byte[] GetBytes(this double value) {
        return BitConverter.GetBytes(value) ;
    }
    public static byte[] GetBytes(this float value) {
        return BitConverter.GetBytes(value) ;
    }
}

他の目的でジェネリッククラスが本当に必要な場合は、Ericが最初にオブジェクトに値を型キャストする場所で述べたように、汚い「二重型キャスト」を実行してください。

于 2013-04-11T21:20:59.270 に答える
2

どうするGenericClass<DateTime>?むしろ、バイトを取得する方法を知っているクラスの個別のセットがあるようです。そのため、共通の作業をすべて行う抽象基本クラスを作成し、メソッドをオーバーライドして間で変化する部分を指定する 3 つの具象クラスを作成します。彼ら:

public abstract class GenericClass<T>
{
    private T _value;

    public void SetValue(T value)
    {
        _value = value;
    }

    public byte[] GetBytes()
    {
        return GetBytesInternal(_value);
    }

    protected abstract byte[] GetBytesInternal(T value);
}

public class IntClass : GenericClass<int>
{
    protected override byte[] GetBytesInternal(int value)
    {
        return BitConverter.GetBytes(value);
    }
}

public class DoubleClass : GenericClass<double>
{
    protected override byte[] GetBytesInternal(double value)
    {
        return BitConverter.GetBytes(value);
    }
}

public class FloatClass : GenericClass<float>
{
    protected override byte[] GetBytesInternal(float value)
    {
        return BitConverter.GetBytes(value);
    }
}

これは、既知の 3 つの型のクリーンで厳密に型指定された実装を提供するだけでなく、他の人がサブクラスGeneric<T>化して の適切な実装を提供するための扉を開いたままにしGetBytesます。

于 2013-04-12T21:29:29.433 に答える