【问题标题】:Why overloading does not occur?为什么不会发生超载?
【发布时间】:2010-10-17 12:56:28
【问题描述】:

我有以下课程:

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&lt;T&gt;() 中递归循环,直到发生堆栈溢出。

【问题讨论】:

    标签: c# .net generics overloading


    【解决方案1】:

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

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

    【讨论】:

    • 这是 C# 必须提供的最佳实践吗?编译器还说:错误 2 无法将类型 'T' 转换为 'Microsoft.Crm.Sdk.CrmDateTime' C:\Documents and Settings\omerk\My Documents\Visual Studio 2008\Projects\Agile.CRMActiveRecord\Agile.CRMActiveRecord\CrmToRealTypeConverter .cs 14 28 敏捷.CRMActiveRecord
    • @the_drow:您的 Crm* 类是否具有共同的基本类型(对象除外)?我想说最好的做法是将 Convert 作为虚拟(可能是抽象)方法添加到基类中,该方法在每个类中都被覆盖。如果这些类不是您创建的,这当然是不可能的。
    • 我没有创建这些类,而且由于设计不好,它们也没有共同的基类。
    • 然后使用扩展方法。请参阅下面的答案。
    【解决方案2】:

    后期绑定不会像您想象的那样发生;编译器将public object Convert&lt;T&gt;(T obj) 方法中对Convert(obj) 的调用绑定到same 方法(递归调用)。您似乎期望的行为是 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 });
    }  
    

    【讨论】:

    • 我总是用反射方法得到 null 方法。编辑:没关系
    • 确实,是程序员的bug,不是程序的bug
    【解决方案3】:

    您应该遵循的模型是 .Net Convert 类中的模型,您没有理由将构造函数设为泛型,它不会带来任何好处。将转换例程更改为静态方法,将类本身更改为静态:

    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】:

    发生这种情况是因为编译器直到运行时才知道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
        }
    }
    

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

    【讨论】:

      猜你喜欢
      • 2019-09-13
      • 1970-01-01
      • 1970-01-01
      • 2019-12-16
      • 1970-01-01
      • 2017-08-16
      • 1970-01-01
      • 2015-09-23
      • 2013-02-17
      相关资源
      最近更新 更多