2012-02-14 18 views
0

私はMedusaを更新して、現在はList<DbParameter>を使用しているどこでも装飾されたPOCOを使用できるようにしようとしています。私が実行している問題は、間違ったオーバーロードが呼び出されているということです。ここで私が見ているかの簡単な例です。この場合はgenericパラメータを渡すと、間違ったオーバーロードが発生する

void Run() 
{ 
    CallDoSomething<Program>("Hello World", new object()); 
    CallDoSomething<Program>("Hello World2", new List<int>()); 
} 

// `DoSomething<T>` represents the functions that do the heavy lifting 
public T DoSomething<T>(string someString, List<int> ints) where T : class 
{ 
    Console.WriteLine("List<int>: {0}", someString); 
    return default(T); 
} 
public T DoSomething<T>(string someString, object ints) where T : class 
{ 
    Console.WriteLine("object: {0}", someString); 
    // In my real implementation, this turns the object to a typed List<T> 
    // and passes it to the previous overload. 
    return default(T); 
} 

// We're trying to refactor the code in this method to reduce code duplication in 
// the `CallDoSomething<T>` methods that will actually be called by the end user 
internal T CallDoSomething<T, U>(string someString, U ints) where T : class 
{ 
    // Do a bunch of stuff here that would otherwise be duplicated by the `CallDoSomething<T>` methods 
    return DoSomething<T>(someString, ints); 
} 

public T CallDoSomething<T>(string someString, List<int> ints) where T : class 
{ 
    return CallDoSomething<T, List<int>>(someString, ints); 
} 
public T CallDoSomething<T>(string someString, object ints) where T : class 
{ 
    return CallDoSomething<T, object>(someString, ints); 
} 

、出力結果は次のとおりです。

object: Hello World 
object: Hello World2 

私はそれがあることを期待していた中:

object: Hello World 
List<int>: HelloWorld2 

両方ともオブジェクトであるので、両方のケースがobjectパラメータを取るオーバーロードに向けられていたことは意味があります。私はこれが起こっていると思う(私が知っているところから)ジェネリックスとオーバーロードの解決は、実行時ではなくコンパイル時に処理されます。

私に来た最初の選択肢はReflectionを使用してCallDoSomething<T, U>で呼び出しを動的に呼び出すことでしたが、それはあまりにも汚いと感じました。代わりに、私が思いついた解決策は、デリケートをCallDoSomething<T, U>に渡すことです。これはうまくいくようです。ここでは次のようになります。

void Run() 
{ 
    CallDoSomething<Program>("Hello World", new object()); 
    CallDoSomething<Program>("Hello World2", new List<int>()); 
} 

public T DoSomething<T>(string someString, List<int> ints) where T : class 
{ 
    Console.WriteLine("List<int>: {0}", someString); 
    return default(T); 
} 
public T DoSomething<T>(string someString, object ints) where T : class 
{ 
    Console.WriteLine("object: {0}", someString); 
    return default(T); 
} 

internal delegate T DoSomethingDelegate<T, U>(string someString, U ints) where T : class; 
internal T CallDoSomething<T, U>(string someString, U ints, DoSomethingDelegate<T, U> doSomething) where T : class 
{ 
    // Do a bunch of stuff here that would otherwise be duplicated by the `CallDoSomething<T>` methods 
    return doSomething(someString, ints); 
} 

public T CallDoSomething<T>(string someString, List<int> ints) where T : class 
{ 
    return CallDoSomething<T, List<int>>(someString, ints, DoSomething<T>); 
} 
public T CallDoSomething<T>(string someString, object ints) where T : class 
{ 
    return CallDoSomething<T, object>(someString, ints, DoSomething<T>); 
} 

これが動作しているようですし、それがコードの重複を大量に削除されますが、それはコードがかなり複雑になります。この問題にアプローチするにはよりよい方法がありますか?

答えて

4

はい、オーバーロードはコンパイル時に解決されます。あなたはあなたはこのように、C#4を使用している場合、それは実行時に評価されるように強制することができます:

internal T CallDoSomething<T, U>(string someString, U ints) where T : class 
{ 
    dynamic d = ints; 
    return DoSomething<T>(someString, d); 
} 

しかし、個人的に私はあなたがおそらくできれば、あなたの設計を簡素化するために試してみました。この種のものは乱雑になる非常に

+0

私は.NET 4に入っていますので、「動的」は私の心を邪魔しましたが、 'ints'はすでに一般的なので大きな違いはないと思いました。しかしそれは意味をなさない。デザインを簡素化するためのアドバイスはありますか? –

+0

@ M.Babcock:ジェネリックとダイナミックは非常に異なります。ジェネリックスは、*コンパイル時により多くの型情報を提供することです。動的なのは、*実行時により多くの型情報を使用することです。デザインを簡素化するために、より多くの文脈を知らなくては言い難いです。 –

+0

ご協力ありがとうございます。私はたぶん、代理人に固執して.NET 4の外部での使用を許可します。デリゲートのソリューションが機能しているように見えますが、このような理由で*ない*を知っていますか? –

関連する問題