传递泛型参数会导致调用错误的重载

时间:2012-02-14 14:59:53

标签: c# oop

我正在尝试更新Medusa以允许在当前使用List<DbParameter>的任何位置使用已修饰的POCO。我遇到的问题是调用了错误的重载。这是我所看到的一个简单例子:

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

这似乎有效,它删除了大量的代码重复,但它使代码相当复杂。有没有更好的方法来解决这个问题?

1 个答案:

答案 0 :(得分:4)

是的,重载在编译时解决。如果您正在使用C#4,可以强制在执行时对其进行评估,如下所示:

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

但是,如果可能,我个人会尝试简化您的设计。这种事情很快就会变得很乱非常