2012-01-13 5 views
7

要するに、タイプパラメータ(T)がstringまたはint[]に制限されている汎用メソッドを定義できますか?擬似C#では、私は何をしたいです:ジェネリック型パラメータをStringまたはArrayに制限することは可能ですか

public static int MyMethod<T> (T arg1, T arg2) 
    where T : (has an indexer that returns an int) { 
    // stuff with arg1[i], arg2[j], etc... 
} 

なお、C#で、原因に内蔵されたint、次の式にcharからの暗黙的な変換と一緒に(charを返す)stringインデクサsourcestringint[]であるかを正確に同じことを意味します

スレッド Constrain generic extension method to base types and stringパー
int someval = source[index]; 

私はちょうどに関連付けられていないタイプのリストを作ることができない実現します制約条項。 int[]stringは両方ともT: IEnumerable<int>に従いますが、IEnumerable<T>では、実装者がインデクサーを必要としません。これは、両方のタイプから使用している共通の機能です。

これは、Damerau-Levenshtein距離アルゴリズムを高速に実装するなど、高度に最適化された文字列解析と解析機能を構築することを目的としています。私の文字列をintの配列に変換すると、(D-Lアルゴリズムのように)文字単位の処理が繰り返し実行される場合があります。これは主に、intの値を比較すると、charの値よりもはるかに高速です。

有効な単語は「時々」です。ストリング上で直接操作し、最初に変換してintの配列にコピーするコストを回避する方が速い場合もあります。だから私は宣言を除いて真に同じメソッドを持っています。

もちろん、私はdynamicを使うことができますが、ランタイムチェックによるパフォーマンスの低下は、そのメソッドの構築で得られた利益を完全に破壊します。 (I をテストしました)。

+0

であなたはそれのための小さなラッパークラスを記述する必要がありますので、

残念ながら、stringは、IList<char>を実装していません。 'T 'を静的に*どちらか一方の型として扱うのは理にかなっていますか?制約のポイントは、 'T'型の変数を操作して最小限の型互換性を期待できるようにすることです。相互に互換性のない2つの型に制約することは奇妙です。 –

+0

@ kirk-woll:私が詳しく説明しているように、それらは私の目的のために相互に互換性がありません。それどころか、彼らは同じです。私は、あるバージョンのパラメータが 'string'として宣言され、他のバージョンで' int [] 'として宣言されていることを除いて、100%同一のメソッドを構築しています。どちらの場合も、コレクションの各メンバーを整数として操作しています。 –

+1

いいえ 'string'と'(任意の) 'Array'が' object'で統一されています。文字列から 'char []' *を作成することはできますが、文字列はC#の 'char []'それは、コピーされた内容を持つ* new *配列オブジェクトです。 –

答えて

4

あなたが持つことができません(...または多分過負荷で、あなたのアイデアを得る)あなたのメソッドのシグネチャ

public static int MyMethod<T> (T[] arg1, T[] arg2) 

を作成し、文字列引数を渡す前にString.ToCharArray()を使用することができます「型にはインデクサが必要です」という制約があります。

ただし、「型がインデクサーを持つインターフェイスを実装する必要があります」という制約を設定できます。このようなインターフェースは、例えば、IList<char>であるかもしれない。

sealed class StringWrapper : IList<char> 
{ 
    public string String { get; private set; } 
    public StringWrapper(string str) { String = str; } 
    public static implicit operator StringWrapper(string str) 
    { 
     return new StringWrapper(str); 
    } 

    public char this[int index] 
    { 
     get { return String[index]; } 
     set { throw new NotSupportedException(); } 
    } 

    // Etc.... need to implement all the IList<char> methods 
    // (just throw NotSupportedException except in the ones that are trivial) 
} 

そしてあなたは、このようなあなたのメソッドを宣言することができます:

public static TElement MyMethod<TCollection, TElement>(TCollection arg) 
    where TCollection : IList<TElement> 
{ 
    return arg[0]; 
} 

[...] 

MyMethod<StringWrapper, char>("abc")  // returns 'a' 
MyMethod<int[], int>(new[] { 1, 2, 3 }) // returns 1 
+0

これは実際には機能しました。 '暗黙の演算子'ステートメントの非常に巧妙な使用。 'StringWrapper'の例を変更して' IList 'を実装し、自分のメソッドを' T:IList 'に制限し、メソッド' string'と 'int []'をコンパイルしました。残念ながら、それはまだ非常に遅いので、私は実際にそれを使用しません。 ( '動的'と提示された 'IList'ラッパーのバージョンは両方とも明示的な' int [] 'と' string'オーバーロードよりも2.8倍遅かったです)。 –

+0

私はあなたが「ダイナミック」ほど遅いと言うことに非常に驚いていますが、あなたはそれをテストしたようですので、私はあなたを信じています。私は、これはダイナミックが驚くほど速く、インタフェースメソッドのディスパッチが驚くほど遅くないことを意味します。 – Timwi

+0

今日は、より異質なデータの大きなセットでいくつかの異なるテストを行いました。私の結果は非常に異なってきました。一般的なは、明示的なint []のオーバーロードよりわずか5%遅かったです。ジェネリックは明示的な 'string'オーバーロードよりもわずか15%遅かった。 「動的」は明示的な過負荷より約50%遅かった。 –

5

いいえ、C#では、そのような「複合」制約を作成することはできません。

あなたが見せる表現は同じことを意味しますが、その事実をあなたの利益に使うことはできません。 C#ジェネリックスは構文的にはC++テンプレートに似ていますが、完全に異なった働きをするので、インデクサが同じことをするという事実は無関係になります。

もちろん、必要な特定のタイプの2つのオーバーロードを作成することもできますが、それは厄介なコピー&ペーストを意味します。繰り返しを避けるためにこれを抽象化しようとすると、パフォーマンスが悪くなります。

0

あなたは

+0

これに関する唯一の問題は、OPが「高度に最適化された文字列解析と解析機能を構築しています」ということです。これはあまり高速ではありません。 –

+0

悲しいかな、それは文字列インデクサーを使用する目的を破ります。私が配列に変換しようとするならば、私は 'int'の配列に変換するだけで、拡張メソッドで簡単に行えます。 –

関連する問題