7

変更できない外部APIとインターフェイスするコードを書いています。

public class ExternalAPI
{
    public static void Read(byte[] buffer);
    public static void Read(int[] buffer);
    public static void Read(float[] buffer);
    public static void Read(double[] buffer);
}

にデータを読み込むときに正しいオーバーロードされたメソッドを呼び出すことが重要ですbufferが、データが読み込まれると、一般的に処理します。それを行うコードでの私の最初のパスは次のとおりです。

public class Foo<T>
{
    T[] buffer;

    public void Stuff()
    {
        ExternalAPI.Foo(buffer);
    }
}

ただし、C#はからT[]に変換されませんbyte[]T明示的に表すことができるタイプを列挙する方法はありますか?句を使ってみましたwhere T :が、言う方法がないようwhere T : {byte, int, float, double and nothing else ever}です。

ここでのアドバイスに従って:数値型に一致するジェネリック制約、ジェネリックobjectに制約を追加し、パラメーターとしてをとるシミュレートされたAPIにジェネリックメソッドも追加しました

public class ExternalAPI
{
    public static void Read(object buffer);
    public static void Read(byte[] buffer);
    public static void Read(int[] buffer);
    public static void Read(double[] buffer);
}

public class Foo<T> where T: struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
{
    T[] buffer;

    public void Stuff()
    {
        ExternalAPI.Read(buffer);
    }
}

Foo(object buffer)これはコンパイルされて正常に実行されますが、がである場合でも、これまでに呼び出される唯一のメソッドTはですbyte。呼び出し元のクラスがジェネリックである場合に、非ジェネリッククラスのメソッドに最も具体的なオーバーロードを使用させる方法はありますか?

4

3 に答える 3

3

私は以前にこのような状況にありましたが、私が思いついた唯一の解決策は、タイプに対してテストしT、適切な関数を呼び出すことです。

public class Foo<T>
{
    T[] buffer;

    public void Stuff()
    {
        var type = typeof(T);

        if (type == typeof(int[]))
        {
            ...
        }
        else if (type == typeof(double[]))
        {
            ...
        }
    }
}
于 2013-01-04T18:53:48.270 に答える
2

私はそれが悲しくて冗長な解決策であることを知っています、しかし私はそれがこれより良い方法で行われることができないと思います:

public void Stuff()
{
    var bufferBytes = buffer as byte[];
    if (bufferBytes != null)
    {
        ExternalAPI.Read(bufferBytes);
        return;
    }
    var bufferInts = buffer as int[];
    if (bufferInts != null)
    {
        ExternalAPI.Read(bufferInts);
        return;
    }
    var bufferDoubles = buffer as double[];
    if (bufferDoubles != null)
    {
        ExternalAPI.Read(bufferDoubles);
        return;
    }
    ExternalAPI.Read(buffer);
}

キャストをできるだけ少なくしてパフォーマンスを向上させるには、チェックを並べ替えて、最も頻繁に呼び出されるチェックをif/elseチェーンの一番上に配置します。モデルによってコードを短くすることができたとしてもif (buffer is byte[]) ExternalAPI.Read(buffer as byte[]); else...、それは無駄なオーバーヘッドを表します(基本的にbuffer2回キャストするため)。

于 2013-01-04T18:54:12.280 に答える
2

呼び出し元のクラスがジェネリックである場合に、非ジェネリッククラスのメソッドに最も具体的なオーバーロードを使用させる方法はありますか?

通常、過負荷の解決はコンパイル時に行われます。の制約T、つまりwhere T: struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable、は、取り込んでいるもの以外のオーバーロードを指すことができないため、それobjectが呼び出されるオーバーロードです。

を使用する場合はdynamic、実行時に過負荷の解決が行われます。dynamic次のように、引数をにキャストするだけです。

public void Stuff()
{
  ExternalAPI.Read((dynamic)buffer);
}

これの悪い点は、プログラムの実行時に過負荷の解決を設定する必要があるため、処理が遅くなることです。ただし、が、などTのいずれかである場合、対応するオーバーロード、などが呼び出されます。がサポートされていないものであり、にオーバーロードが存在しない場合、これは実行時に失敗し、例外をスローしますが、メソッドが実行されるまでは失敗しません。byteintbyte[]int[]TobjectExternalAPIStuff

Foo<T>別の解決策は、正しいメソッドを保持するためのデリゲートをクラスに装備することです。次のようになります。

T[] buffer;

readonly Action<T[]> readMethod;

public void Stuff()
{
  readMethod(buffer);
}

//constructor
public Foo(Action<T[]> readMethod)
{
  this.readMethod = readMethod;
}

しかし、その後、人々は次のようにクラスをインスタンス化する必要があります。

new Foo<byte>(ExternalAPI.Read)

適切なオーバーロードは、人々がのインスタンスを作成した場所ごとにコンパイル時に選択されますFoo<>。で定義されreadMethodたメソッドを表すユニキャストデリゲートであるインスタンスコンストラクターにチェックを追加できます。Readtypeof(ExternalAPI)

3番目の解決策は、readMethodフィールドを作成しstatic、を初期化する静的コンストラクターを含めることreadMethodです。byteこれは見苦しいように見えますが、静的コンストラクターは、使用したタイプ( 、、など)ごとに1回だけ実行intされます。静的コンストラクターは、ユーザーが間違ったを使用した場合に例外をスローする可能性がありますT。静的コンストラクターは次のようになります。

static Foo()
{
  if (typeof(T) == typeof(byte))
    readMethod = (Action<T[]>)(Delegate)(Action<byte[]>)ExternalAPI.Read;
  else if (typeof(T) == typeof(int))
    readMethod = (Action<T[]>)(Delegate)(Action<int[]>)ExternalAPI.Read;
  else if (typeof(T) == typeof(float))
    readMethod = (Action<T[]>)(Delegate)(Action<float[]>)ExternalAPI.Read;
  else if (typeof(T) == typeof(double))
    readMethod = (Action<T[]>)(Delegate)(Action<double[]>)ExternalAPI.Read;
  else
    throw new Exception("The type parameter T can't be " + typeof(T));
}

編集:以下の最初のコメントに触発されて、静的コンストラクターに代わりにリフレクションを使用させる試みがあります:

static Foo()
{
  var methInfo = typeof(ExternalAPI).GetMethod("Read", new[] { typeof(T[]), });
  if (methInfo == null)
    throw new Exception("ExternalAPI has no suitable method for " + typeof(T[]));
  readMethod = (Action<T[]>)Delegate.CreateDelegate(typeof(Action<T[]>), methInfo);
}
于 2013-01-04T21:07:19.107 に答える