【问题标题】:'Why' and 'Where' Generics is actually used?“为什么”和“在哪里”泛型实际上被使用了?
【发布时间】:2010-03-19 06:42:24
【问题描述】:

我知道泛型用于实现类型安全,并且我经常读到它们主要用于自定义集合。但是为什么我们实际上需要让它们通用呢?

例如,

为什么我不能使用string[] 而不是List<string>

假设我声明了一个泛型类,它有一个泛型类型参数 X。

T x;

如果我为类提供了一个方法

x = x + 1;

实际上是什么意思?我不知道T实际上会是什么,我也不知道x = x + 1实际上会做什么。

如果我无法在我的方法中进行自己的操作,那么泛型将如何帮助我?

我已经研究了很多书呆子的答案。如果有人能在这方面提供一些清晰的见解,将不胜感激。

问候, NLV

【问题讨论】:

  • Dictionary<Key,Value>?
  • 大错误:数组不能替换所有数据结构。数组只是平台提供的基本数据类型。

标签: c# .net generics


【解决方案1】:

您的问题会自行回答。 数组类型本身就是泛型类型的一种形式。如果我们在 CLR v1.0 中放置一个泛型类型系统,我敢打赌不会有特殊的数组类型,只会有Array<T> 以及 List<T> 等等。由于 CLR 的第一个版本没有泛型类型,我们将最重要和最明显的泛型类型——数组类型放入其中。因此,其中有一大堆专门用于处理数组的专用代码,这是 CLR 在 v1.0 中支持的一种泛型类型。

由于数组本质上是一种泛型类型,因此您的问题本身就可以回答:泛型类型的一般原因与促使创建数组类型模式的原因相同。即:数组类型以特定方式放大其底层类型的能力

int 代表一个数字。 int[] 表示数字的集合;我们将数字的概念放大为数字集合的概念。客户代表客户。 Customer[] 表示客户集合的概念。我们将客户的概念扩大到了收集客户的概念。 从类型 T 到类型 T[] 的映射表示将一个类型的实例泛型放大为该类型实例的集合的抽象概念。

同样的理由激发了所有泛型类型。 Nullable<T> 类型将类型放大到“这个东西可能是该类型的一个实例”的概念。 IComparable<T> 类型将类型放大为“可以相对于另一个实例对这种类型的实例进行排序”的概念。等等。每个泛型类型就像数组模式:它表示将一个类型放大为一个新类型,该类型提供对该类型的新操作。

简而言之:泛型类型系统的目的是让您能够发明自己的类型放大,并使用类型系统操作这些放大

【讨论】:

    【解决方案2】:

    当我们需要“高阶”多态性时,我们使用泛型,类似于编译时duck typing。当我们说Foo<T> 时,我们的意思是Foo<> 依赖于任何可能的T 可以具有的某些属性,并且给定的T 具有此属性,或者代码无法编译。

    当我们说x = x + 1 时,我们暗示T 能够添加1,并且返回类型是T

    【讨论】:

    • +1 表示“高阶多态性”,具体称为“参数多态性”。
    【解决方案3】:

    但是为什么我们实际上需要让它通用呢?

    如果您愿意,您还可以创建许多自定义类:StringListIntListDoubleList 等。但是泛型的重点是您可以定义一次类,它适用于所有对象.

    x = x + 1 其中x 是泛型类型T 需要实际的real 类型以自然地支持与int 的加法,或者将operator + 与int 重载为第二种。

    【讨论】:

      【解决方案4】:

      实际上,我正在一个网站 atm 上工作,泛型使我的工作变得更加轻松。我需要从数据库中访问大量信息,因为整个站点都是数据库驱动的,所以我真的很想有一个类来实现数据库功能。这个类被更多知道如何处理各自数据的类继承。

      例如,我有一个处理 Products 数据的 Products 类和一个处理主题等的 Themes 类。好吧,所有这些类都需要一种通用的读写格式。所以,我创建了一个记录类来处理这个。

      现在,泛型的乐趣从这里开始。我创建了一个产品类、主题类等。具有强类型成员,例如 Name、Manufacturer、ThemeId,但是 Records 类不知道如何处理它们。因此,数据库类使用与特定类、产品、主题等相关的类型进行实例化。而且Record类也使用了Generic类型,所以我可以写代码如...

      Product.Name = "Cool Product";
      

      然后像...一样存储它

      Products.InsertRecord(Product coolProduct);
      

      它不仅节省了大量输入,而且使我能够拥有一个处理所有脏活的类,而这些小的“存根”类为我提供了一个可读的强类型接口。

      无论如何,对于冗长的帖子感到抱歉。希望这可以帮助您至少在这种情况下理解泛型的力量。

      【讨论】:

        【解决方案5】:

        对于您的第一个问题,如果您想动态地将字符串添加到列表中,您可以选择 List<string> 而不是 string[] - 数组是固定大小的。

        【讨论】:

          【解决方案6】:

          因为虽然支持 string[](即扣除自定义数组类型),所以数组具有: * 功能比 List 甚至 IList 少 * 是一个数组,因此这不适用于其他类型的集合。字典呢?

          如果我不能自己做 我的方法中的操作如何 仿制药将帮助我 还是?

          这有点像问“如果我经营一家素食餐厅,我可以从供应商那里购买牛排有什么好处?”。

          基本上,当您的项目变得更加复杂时,您将在方法中编写自己的操作。你会做所有有意义的事情。

          【讨论】:

            【解决方案7】:
            public foobar<T>{
                public Action<string,T> SomeAction;
                public string SomeString;
                public foobar(Action<string,T> s,string m){
                     SomeAction=s;
                     SomeString=m;
                }
            
            
                public void Run(T foo){
                     SomeAction(SomeString,foo);
                }
            }
            

            如果没有泛型,您将不得不为每种可能性编写一个不同的类,或者进行一些其他复杂的操作才能获得所需的结果。泛型让事情变得更加优雅。

            【讨论】:

              【解决方案8】:

              string[] 给了我们一个固定大小的数组。要获得动态大小的列表,我们需要一个类。因此,如果我们想要一个字符串列表,我们可能必须创建一个名为 StringList 的类。对于双打,DoubleList。等等。但是这些类是那些类型所独有的呢?没有。因此,我们创建了一个通用类 List,它可以接受任何类型。不必为不同的类型一遍又一遍地重新创建同一个类,泛型为我们节省了一些工作。

              至于x = x + 1 问题,您必须期望T 有一个+ 运算符,它在右侧接受一个int。如果没有,它将引发运行时错误。如果是这样,那么该代码会将 x 分配给 +operator 的输出,并将 x1 作为参数。

              在许多其他情况下,没有执行唯一操作,并且类/方法必须与不止一种类型一起使用。这些也是泛型重要且有用的情况。

              您可以自行决定何时何地在代码中使用它们。如果您遇到问题,他们是最好的答案,那就太好了。使用它们。否则,不要。只是不要取消它们作为解决方案的资格,因为它们从来都不是您特定问题的答案。

              【讨论】:

                【解决方案9】:

                我自己的工作经验中的一个很好的例子是管理 Linq-To-SQL 类型。创建新数据库对象时我必须做的一项基本操作是:

                1. 检查本地缓存中是否存在请求的对象。
                2. 否则,请检查数据库中是否存在请求的对象。
                3. 否则,使用提供的参数实例化一个新对象。

                在这个特定的项目中,大约有 100 种不同的对象类型,因此为每种类型编写此代码充其量是很麻烦的,即使使用脚本也是如此,如果我们必须更改方法的某些内容,我们该怎么办?

                使用泛型和约束,我可以做这样的事情:

                public class SQLObjectManager<TEntity> where TEntity : class, new()
                {
                    public TEntity GetObject(int year, int stateID)
                    {
                        if( /* entity found in cache */)
                            return GetFromCache(year,stateID);
                        if( /* entity found in db */)
                            return GetFromDB(year,stateID);
                        TEntity entity = new TEntity();
                        entity.Year = year;
                        entity.StateID = stateID;
                        return entity;
                    }
                }
                

                除了编译时类型检查的好处之外,我现在有一个单独的函数来实现和维护,而不是为我的数据库中的每个表创建一个新版本。

                【讨论】:

                  【解决方案10】:

                  请注意,如果您来自 C++ 背景,那么 C# 泛型和 C++ 模板是相似的概念。不过,我相信它们实际上工作方式不同。

                  泛型用于编写可跨不同类型重用的代码。泛型,提高类型安全性,减少铸造和装箱。

                  假设你需要一堆整数;一个解决方案是提出一个整数堆栈类。然后对于每个所需的数据类型(浮点数、字符串等),您将编写单独的类版本;在这里你会意识到这会导致代码重复。

                  另一种解决方案是编写一个堆栈,通过使用对象作为元素类型进行泛化:

                  跟随怎么样?

                  MyGenericStack<string> stringStack = new MyGenericStack<string>();
                  stringStack.Push("One");
                  stringStack.Push("Two");
                  stringStack.Push("Three");
                  
                  MyGenericStack<int> intStack = new MyGenericStack<int>();
                  intStack.Push(1);
                  intStack.Push(2);
                  intStack.Push(3);
                  
                  MyGenericStack<CustomObject> customObjectStack = new MyGenericStack<CustomObject>();
                  customObjectStack.Push(new CustomObject(1));
                  customObjectStack.Push(new CustomObject(2));
                  customObjectStack.Push(new CustomObject(3));
                  

                  下面是泛型类

                  public class MyGenericStack<T>
                  {
                      int position;
                      T[] data = new T[10];
                      public void Push(T obj) { data[position++] = obj; }
                      public T Pop() { return data[--position]; }
                  
                      public void Show()
                      {
                  
                          for (int i = 0; i < data.Length; i++)
                          {
                              //Console.WriteLine("Item: {0}", data[i]);
                              //MessageBox.Show(String.Format("Item: {0}", data[i]));
                          }
                      }
                  }
                  

                  是的,我知道,现在你会 too 爱上泛型!

                  顺便说一句,如果您打算比较继承和泛型,请记住,继承具有基类型的可重用性,而泛型表示模板类型的可重用性。

                  【讨论】:

                    猜你喜欢
                    • 2022-12-03
                    • 2023-01-26
                    • 2017-10-02
                    • 2023-03-31
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-12-31
                    • 2016-10-12
                    • 2013-06-02
                    相关资源
                    最近更新 更多