【问题标题】:What are the advantages of using generics in method signatures?在方法签名中使用泛型有什么好处?
【发布时间】:2009-03-19 01:18:25
【问题描述】:

(感谢大家的回答,here is my refactored example,这是 StackOverflow 关于单一职责原则的另一个问题。)

从 PHP 到 C#,这种语法令人生畏:

container.RegisterType<Customer>("customer1");

直到我意识到它表达的意思是一样的:

container.RegisterType(typeof(Customer), "customer1");

正如我在下面的代码中演示的那样。

所以这里使用泛型有什么原因(例如,在整个 Unity 和大多数 C# IoC 容器中),除了它只是一种更简洁的语法,即你不需要 typeof() 时发送类型?

using System;

namespace TestGenericParameter
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            container.RegisterType<Customer>("test");
            container.RegisterType(typeof(Customer), "test");

            Console.ReadLine();
        }
    }

    public class Container
    {
        public void RegisterType<T>(string dummy)
        {
            Console.WriteLine("Type={0}, dummy={1}, name of class={2}", typeof(T), dummy, typeof(T).Name);
        }

        public void RegisterType(Type T, string dummy)
        {
            Console.WriteLine("Type={0}, dummy={1}, name of class={2}", T, dummy, T.Name);
        }

    }

    public class Customer {}
}

//OUTPUT:
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer

【问题讨论】:

    标签: c# generics


    【解决方案1】:

    泛型非常有用的一个原因是泛型类型参数用作参数的类型或方法的返回类型。 这意味着,您可以编写类似的方法

    public T GetAs<T>(string name)
    

    编译器可以检查返回类型,有时可以避免装箱值类型。 调用者会写:

    int value = GetAs<int>("foo");
    

    如果没有泛型,您将不得不编写

    public object GetAs(Type t, string name)
    

    调用者必须再次转换结果:

    int value = (int)GetAs(typeof(int), "foo");
    

    【讨论】:

      【解决方案2】:

      主要原因是编译时的类型安全。如果您传递两个 Type 对象,则您将责任放在开发人员而不是编译器身上。

      这也是许多 IoC 容器使用它的原因,因为如果具体类型没有继承抽象类型,您的编译器会报错。

      public void Register<TAbstract, TConcrete>() where TConcrete : TAbstract
      {
      }
      

      此代码仅在 TConcrete 实现或继承 TAbstract 时才有效。如果此方法采用两个 Type 参数,则您的方法应验证此关系。

      【讨论】:

        【解决方案3】:

        一个简单的答案是在可能的情况下类型推断

        如果方法签名中使用了泛型类型,则可以省略它,因为可以推断出类型:

        void SomeMethod<T>(T x, T y) where T : IComparable<T> {
            Console.WriteLine("Result: {0} to {1} is {2}", x, y, x.CompareTo(y));
        }
        

        所以用法就简化了:

        SomeMethod(3, 4);         // instead of SomeMethod<int>(3, 4);
        SomeMethod("one", "two"); // instead of SomeMethod<string>("one", "two");
        

        如果方法签名中没有使用泛型类型参数,则无法进行类型推断:

        var emptySequence = Enumerable.Empty<int>();
        

        【讨论】:

          【解决方案4】:

          我认为主要用途之一是带有参数和返回值的类型安全。在您的示例案例中,泛型没有太多用处,因为输入/输出类型(字符串)与泛型案例(客户)不匹配。

          更合适的用法可能是:

          public T RegisterType<T>(string name) 
          {
              T obj = new T();
              obj.DoSomething();
              return obj;
          }
          

          或许

          public void DoSomething<T>(T obj) 
          {
              //operate on obj
          }
          

          【讨论】:

          • 这听起来很有用,但尝试实现它,行“T obj = new T();”给我“无法创建变量类型'T'的实例,因为它没有 new() 约束”。我错过了什么?
          • 你需要像我的回答一样添加一个约束:public T CreateNew&lt;T&gt;() where T : new()
          【解决方案5】:

          例如,比较使用typeof 选项和泛型创建类型实例所需的代码。或者返回该类型的一个实例。或者接受该类型的一个实例作为参数。或者在该类型的实例上设置一个属性。

          一般来说,如果您只使用类型本身,您可以接受类型参数。如果您想对类型的 instance 做任何事情,请使用泛型。

          使用泛型的另一个原因是如果您想对类型应用约束。例如,您可以要求该类型实现一个或多个接口、继承另一种类型、作为引用类型或值类型、具有默认构造函数或以上的某种组合。编译器将强制执行此操作,因此您无法构建不符合您要求的代码。

          【讨论】:

            【解决方案6】:

            如果您不使用泛型,则必须为要支持的每种类型重载一个方法,或者您必须将参数作为对象接受并执行强制转换逻辑。

            【讨论】:

              【解决方案7】:

              我想说最好的理由是类型安全,使用“where”关键字来确保泛型类型是某种类型(或子类/实现者)。使用“typeof”可以让你发送任何东西。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2010-09-09
                • 1970-01-01
                • 2022-06-10
                • 2011-03-26
                • 1970-01-01
                • 2013-08-08
                • 1970-01-01
                相关资源
                最近更新 更多