为什么不发生超载?

时间:2010-10-17 12:56:28

标签: c# .net generics overloading

我有以下课程:

class CrmToRealTypeConverter : IConverter
{
    #region IConverter Members

    public object Convert<T>(T obj)
    {
        return Convert(obj);
    }

    #endregion

    private DateTime? Convert(CrmDateTime obj)
    {
        return obj.IsNull == false ? (DateTime?)obj.UserTime : null;
    }

    private int? Convert(CrmNumber obj)
    {
        return obj.IsNull == false ? (int?)obj.Value : null;
    }

    private decimal? Convert(CrmDecimal obj)
    {
        return obj.IsNull == false ? (decimal?)obj.Value : null;
    }

    private double? Convert(CrmDouble obj)
    {
        return obj.IsNull == false ? (double?)obj.Value : null;
    }

    private float? Convert(CrmFloat obj)
    {
        return obj.IsNull == false ? (float?)obj.Value : null;
    }

    private decimal? Convert(CrmMoney obj)
    {
        return obj.IsNull == false ? (decimal?)obj.Value : null;
    }

    private bool? Convert(CrmBoolean obj)
    {
        return obj.IsNull == false ? (bool?)obj.Value : null;
    }
}

我正在尝试使用concreate类型专门化Convert方法 目前它只是在Convert<T>()中递归循环,直到发生堆栈溢出。

4 个答案:

答案 0 :(得分:3)

多态性不适用于方法调用的参数。您可以使用它来检查obj的类型,将其转换为特定类型然后调用适当的重载。

public object Convert(object obj)
{
    if (obj is CrmDateTime)
        return Convert((CrmDateTime)obj);
    if (obj is CrmNumber)
        return Convert((CrmNumber)obj);
    // ...
}

答案 1 :(得分:2)

延迟绑定不会以您认为的方式发生;编译器将Convert(obj)方法中对public object Convert<T>(T obj)的调用绑定到相同的方法(递归调用)。您似乎期望的行为是CLR将动态选择在运行时执行的最合适的重载,但它不会那样工作。尝试这样的事情:

public object Convert<T>(T obj)
{
   if (obj == null)
       throw new ArgumentNullException("obj");

    var cdt = obj as CrmDateTime;   
    if (cdt != null)
        return Convert(cdt); // bound at compile-time to DateTime? Convert(CrmDateTime)

    var cn = obj as CrmNumber;    
    if (cn != null)
        return Convert(cn); // bound at compile-time to int? Convert(CrmNumber)

    // ...    

    throw new NotSupportedException("Cannot convert " + obj.GetType());
}

如果您愿意,可以在这里使用反射。这样的解决方案看起来像是:

// Making the method generic doesn't really help
public object Convert(object obj) 
{
   if (obj == null)
       throw new ArgumentNullException("obj");

    // Target method is always a private, instance method
    var bFlags = BindingFlags.Instance | BindingFlags.NonPublic;

    // ..which takes a parameter of the obj's type.      
    var parameterTypes = new[] { obj.GetType() };

    // Get a MethodInfo instance that represents the correct overload
    var method = typeof(CrmToRealTypeConverter)
                 .GetMethod("Convert", bFlags, null, parameterTypes, null);

    if (method == null)
        throw new NotSupportedException("Cannot convert " + obj.GetType());

    // Invoke the method with the forwarded argument
    return method.Invoke(this, new object[] { obj });
}  

答案 2 :(得分:2)

你应该遵循的模型是.Net Convert类中的模型,你没有理由让构造函数成为通用的,它没有给表带来任何东西。将转换例程更改为静态方法,将类本身更改为static:

static class CrmToRealTypeConverter : IConverter
{
    #region IConverter Members

    public static DateTime? Convert(CrmDateTime obj)
    {
        return obj.IsNull == false ? (DateTime?)obj.UserTime : null;
    }

    public static int? Convert(CrmNumber obj)
    {
        return obj.IsNull == false ? (int?)obj.Value : null;
    }

    public static decimal? Convert(CrmDecimal obj)
    {
        return obj.IsNull == false ? (decimal?)obj.Value : null;
    }

    public static double? Convert(CrmDouble obj)
    {
        return obj.IsNull == false ? (double?)obj.Value : null;
    }

    public static float? Convert(CrmFloat obj)
    {
        return obj.IsNull == false ? (float?)obj.Value : null;
    }

    public static decimal? Convert(CrmMoney obj)
    {
        return obj.IsNull == false ? (decimal?)obj.Value : null;
    }

    public static bool? Convert(CrmBoolean obj)
    {
        return obj.IsNull == false ? (bool?)obj.Value : null;
    }
}

然后,当您调用其中一个转换方法时,编译器将选择适当的重载来调用:

CrmDateTime crmDate;
CrmToRealTypeConverter.Convert(crmDate);  // Will call the static DateTime? Convert(CrmDateTime obj) overload    
// or 
CrmNumber crmNum;
CrmToRealTypeConverter.Convert(crmNum);  // Will call the static int? Convert(CrmNumber obj) overload
// and so on...

修改: 如果您执行以下操作:

CrmFloat num;
// ...
Object obj = num;
CrmToRealTypeConverter.Convert(obj);

它不起作用,因为编译器不知道匹配重载的类型。你必须施展它并且它会起作用:

CrmToRealTypeConverter.Convert((CrmFloat)obj);

答案 3 :(得分:1)

这是因为编译器在运行时并不知道T的泛型类型,并且在编译时将调用绑定到T = System.Object,并且唯一适合采用System.Object的函数是这个功能本身。但是,在.NET 4中,您可以使用dynamic关键字使运行时根据运行时的T动态选择正确的重载,这就是您要发生的事情。

简单示例:

class Main {
    static void somefunction(System.String str)
    {
        System.Console.WriteLine("In String overload");
    }
    static void somefunction(System.Object obj)
    {
        System.Console.WriteLine("In Object overload");
    }
    static void somegenericfunction<T>(T object)
    {
        somefunction(object);
    }
    static void dynamicfunction<T>(dynamic T object)
    {
        somefunction(object);
    }
    static void main(System.String[] args)
    {
        somegenericfunction("A string"); // Calls Object overload, even though it's a String.
        dynamicfunction("A string"); // Calls String overload
    }
}

请注意,我实际上并没有我的编译器,这可能无法按字面编译,但足够接近。