如果我们使用的所有类通常都继承自某个公共类,为什么还需要泛型呢?

本文关键字:为什么 泛型 我们 继承 如果 | 更新日期: 2023-09-27 18:10:16

大家好。泛型通常像这样使用:

class MyList<T>
{
    public T data;
    public MyList<T> nextElement;
}

为什么不用follow:

class MyList
{
    public object data;
    public MyList nextElement;
}

甚至:

class MyStructure<T> where T : SomeCommonClass
{
    public T data;
    public MyStructure<T> nextElement;
    public MyStructure<T> prevElement;
}

,而是:

class MyStructure
{
    public SomeCommonClass data;
    public MyStructure nextElement;
    public MyStructure prevElement;
}

更新:

嗯,恐怕你没有完全理解我的意思,甚至贬低了我的问题。没有泛型的例子,可以正常工作:
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        MyNode nodeButton = new MyNode("nodeButton", new Button());
        MyNode nodeTextBox = new MyNode("nodeTextBox", new TextBox());
        MyNode nodeCheckBox = new MyNode("nodeCheckBox", new CheckBox());
        MyList myList = new MyList() { nodeButton, nodeTextBox, nodeCheckBox };
        for (int i = 0; i < myList.Count;i++)
        {
            this.Controls.Add(myList[i].Data);
            myList[i].Data.Left = 100 * i;
        }
    }
}
public class MyNode
{
    public MyNode(string name, Control data)
    {
        Data = data;
        Name = name;
    }
    public string Name { get; private set; }
    public Control Data { get; private set; }
}
public class MyList : Collection<MyNode>
{
    protected override void InsertItem(int index, MyNode item)
    {
        base.InsertItem(index, item);
        item.Data.MouseClick += new MouseEventHandler((sender, e) => { MessageBox.Show(item.Name); });
    }
}

与泛型相同的例子,在编译时产生错误:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        MyNode<Button> nodeButton = new MyNode<Button>("nodeButton", new Button());
        MyNode<TextBox> nodeTextBox = new MyNode<TextBox>("nodeTextBox", new TextBox());
        MyNode<CheckBox> nodeCheckBox = new MyNode<CheckBox>("nodeCheckBox", new CheckBox());
        MyList myList = new MyList() { (MyNode<Control>)nodeButton, (MyNode<Control>)nodeTextBox, (MyNode<Control>)nodeCheckBox };
        for (int i = 0; i < myList.Count;i++)
        {
            this.Controls.Add(myList[i].Data);
            myList[i].Data.Left = 100 * i;
        }
    }
}
public class MyNode<T> where T : Control
{
    public MyNode(string name, T data)
    {
        Data = data;
        Name = name;
    }
    public string Name { get; private set; }
    public T Data { get; private set; }
}
public class MyList : Collection<MyNode<Control>>
{
    protected override void InsertItem(int index, MyNode<Control> item)
    {
        base.InsertItem(index, item);
        item.Data.MouseClick += new MouseEventHandler((sender, e) => { MessageBox.Show(item.Name); });
    }
}

根据你的说法,第一个版本是不好的做法,因为类型安全被破坏了,但第二个版本不允许类型转换!

如果我们使用的所有类通常都继承自某个公共类,为什么还需要泛型呢?

在这两种情况下,您都可能丢失大量关于对象的信息,从而丢失大量类型安全。选角并不愉快。您还失去了确保只有的列表包含特定类型的对象的能力。

在使用值类型时也会有明显的性能损失;它们必须从object中装箱和拆箱,这是缓慢的,而且也是不安全的。

事实上,你所建议的曾经(嗯,现在仍然)存在!它叫做ArrayList,非常可怕。

From MSDN

泛型允许您根据其作用的精确数据类型定制方法、类、结构或接口。

当您将某些内容声明为object时,需要在执行特定类型的操作之前将其强制转换为适当的数据类型。同样,在派生类的例子中,如果您只使用基类,您就失去了使用派生类属性的能力,使其变得毫无意义。

在文档的后面,明确地提到了一个优点:

创建泛型类的实例时,指定实际类型来替代类型参数。这将建立一个新的泛型类,称为构造的泛型类,在类型参数出现的任何地方替换您选择的类型。结果是一个类型安全的类,它适合您选择的类型。

这意味着您在代码中获得了类型安全。同时,由于您只需要插入适当的类型,因此您可以获得高度可重用的代码。

参考:http://msdn.microsoft.com/en-us/library/ms172192 (v = vs.110) . aspx

因为您可以使用泛型列表:

int sum = 0;
List<int> list = GetList();
foreach(var item in list) // the compiler knows this is an int
{
    sum += item; // This would not be possible with an `object`
}

请注意,允许编译器知道我的列表是什么类型,使我能够对列表中的对象做一些本来需要强制类型转换的事情。因为我在编译时就知道我在处理什么类型的对象,所以在运行这段代码之前,如果我试图做一些我不允许做的事情,我就可以捕获错误。

泛型提供了很多好处,比如类型安全、避免装箱和拆箱。与使用对象相比,泛型速度快,因为它避免了装箱和拆箱。如果您是泛型的新手,请访问以下链接:

c#泛型简介

泛型(c#编程指南)

更新:

如果您理解类型安全的正确含义,那么您就不难理解Generics完全不允许用户对对象进行类型强制转换。如果你对类型转换如此确定,那么你可以在MyNode类中编写自己的Cast<>()方法,它可以将类型MyNode<T1>转换为MyNode<T2>。如果你知道这个场景,如果你对类型和铸件如此确定,如果你知道未来的变化不会与当前冲突,那么就不要使用Generics。在编程中,我们不可能在每个地方都使用所有东西,这完全取决于场景,以及最适合场景的实践。因此,您应该在最适合的地方使用Generics,并且您想要利用它。我希望你能理解我试图解释的内容。