C#中重载方法的一些总结

新建一个.NET Core控制台项目,我们来看看C#中重载方法的一些注意事项。

C#中多个重载方法之间的参数如果有继承关系,那么调用方法时,会调用与传入参数类型最接近的重载方法

我们来举个例子,下面我们定义了两个重载方法Do,它们的参数类型A和B是继承关系,类B继承类A,那么我们在调用Do方法时,到底调用的是哪一个重载呢?

代码如下:

using System;

namespace NetCoreOverloadParameters
{
    /// <summary>
    /// 类A
    /// </summary>
    class A
    {

    }

    /// <summary>
    /// 类B继承类A
    /// </summary>
    class B : A
    {

    }

    class Program
    {
        /// <summary>
        /// 方法Do
        /// </summary>
        /// <param name="a">参数类型为A</param>
        static void Do(A a)
        {
            Console.WriteLine("Do A");
        }

        /// <summary>
        /// 方法Do
        /// </summary>
        /// <param name="a">参数类型为B</param>
        static void Do(B a)
        {
            Console.WriteLine("Do B");
        }

        static void Main(string[] args)
        {
            A a = new B();
            B b = new B();

            Do(a);//因为传入Do方法的类型是A,所以这里调用Do(A a)
            Do(b);//因为传入Do方法的类型是B,所以这里调用Do(B a)
            Do(new A());//因为传入Do方法的类型是A,所以这里调用Do(A a)
            Do(new B());//因为传入Do方法的类型是B,所以这里调用Do(B a)
            Do(null);//当传入null给Do方法时,这里调用的是Do(B a),说明优先调用的是继承链中参数为子类B的重载方法
            Do((A)null);//因为传入Do方法的类型是A,所以这里调用Do(A a)

            Console.WriteLine("Press any key to end...");
            Console.ReadKey();
        }
    }
}

执行结果如下:

所以我们可以看到,实际上在每次调用Do方法时,C#会选择调用和传入参数类型最接近的重载方法。

如果我们现在注释掉代码中的重载方法Do(B a),由于现在代码中只有一个Do方法了,所以所有的调用都会调用Do(A a):

using System;

namespace NetCoreOverloadParameters
{
    /// <summary>
    /// 类A
    /// </summary>
    class A
    {

    }

    /// <summary>
    /// 类B继承类A
    /// </summary>
    class B : A
    {

    }

    class Program
    {
        /// <summary>
        /// 方法Do
        /// </summary>
        /// <param name="a">参数类型为A</param>
        static void Do(A a)
        {
            Console.WriteLine("Do A");
        }

        /// <summary>
        /// 方法Do
        /// </summary>
        /// <param name="a">参数类型为B</param>
        //static void Do(B a)
        //{
        //    Console.WriteLine("Do B");
        //}

        static void Main(string[] args)
        {
            A a = new B();
            B b = new B();

            Do(a);
            Do(b);
            Do(new A());
            Do(new B());
            Do(null);
            Do((A)null);

            Console.WriteLine("Press any key to end...");
            Console.ReadKey();
        }
    }
}

执行结果如下:

C#中如果有签名相同的泛型方法和非泛型方法,那么C#会优先考虑调用非泛型方法

我们来看看下面这个例子,我们在代码中,定义了三个Do方法,其中一个非泛型方法和两个泛型方法,那么我们在调用Do方法时,C#会优先考虑调用非泛型方法:

using System;

namespace NetCoreOverloadParameters
{
    /// <summary>
    /// 类A
    /// </summary>
    class A
    {

    }

    /// <summary>
    /// 泛型静态类Container<T1>
    /// </summary>
    static class Container<T1>
    {
        /// <summary>
        /// 方法Do
        /// </summary>
        /// <param name="a">参数类型为A</param>
        public static void Do(A a)
        {
            Console.WriteLine("Do A");
        }

        /// <summary>
        /// 方法Do
        /// </summary>
        /// <param name="t1">参数类型为T1</param>
        public static void Do(T1 t1)
        {
            Console.WriteLine("Do T1");
        }

        /// <summary>
        /// 方法Do
        /// </summary>
        /// <typeparam name="T2">类型参数T2</typeparam>
        /// <param name="t2">参数类型为T2</param>
        public static void Do<T2>(T2 t2)
        {
            Console.WriteLine("Do T2");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();

            Container<A>.Do(a);//C#会优先考虑非泛型的方法,来匹配传入Do方法的类型,所以这里会调用重载方法Do(A a),这会导致泛型重载方法Do(T1 t1),无法被调用到
            Container<object>.Do((object)a);//将静态类Container<T1>的类型参数<T1>定义为object后,再给Do方法传入object类型的参数,这样C#就会优先调用泛型重载方法Do(T1 t1)了
            Container<A>.Do<A>(a);//由于这里我们显式声明了类型参数<T2>,所以C#知道我们在这里要调用的是重载方法Do<T2>(T2 t2)

            Console.WriteLine("Press any key to end...");
            Console.ReadKey();
        }
    }
}

执行结果如下:

在这个例子中,我们可以看到,由于我们在Main方法中调用Container<A>.Do(a)时,其可以匹配两个签名相同的重载方法,一个是非泛型方法Do(A a),另一个是泛型方法Do(T1 t1),但是C#优先选择的是非泛型方法Do(A a),这导致了当传入Do方法的参数类型是A时,泛型方法Do(T1 t1)无法被调用到。

关于泛型方法的重载问题,可以参考:Generic methods and method overloading

C#中扩展方法和非扩展方法的重载

关于这个讨论,可以参考:C#中如果类的扩展方法和类本身的方法签名相同,那么会优先调用类本身的方法

原文地址:https://www.cnblogs.com/OpenCoder/p/12485179.html