代码之家  ›  专栏  ›  技术社区  ›  the_drow

为什么不发生过载?

  •  5
  • the_drow  · 技术社区  · 14 年前

    我有以下课程:

    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 回复  |  直到 14 年前
        1
  •  2
  •   Ani    14 年前

    延迟绑定不会像您认为的那样发生;编译器将调用绑定到 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
  •  3
  •   Mark Byers    14 年前

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

    public object Convert(object obj)
    {
        if (obj is CrmDateTime)
            return Convert((CrmDateTime)obj);
        if (obj is CrmNumber)
            return Convert((CrmNumber)obj);
        // ...
    }
    
        3
  •  2
  •   Steve Ellinger    14 年前

    您应该遵循的模型是.NET转换类中的模型,您没有理由使构造函数成为泛型,它不会给表带来任何影响。将转换例程更改为静态方法,将类本身更改为静态:

    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);
    
        4
  •  1
  •   Puppy    14 年前

    这是因为编译器不知道 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
        }
    }
    

    请注意,实际上我手头没有编译器,这可能不是逐字编译,而是足够接近。