如果我们使用的所有类通常都是从一些普通类继承的,那么为什么我们需要泛型?

时间:2014-06-26 03:57:26

标签: c# generics inheritance

美好的一天。泛型通常使用如下:

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); });
    }
}

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

4 个答案:

答案 0 :(得分:9)

在这两种情况下,您都会丢失很多关于该对象的信息,并且有很多类型的安全性。铸造不愉快。您也无法确保列表包含特定类型的对象。

使用值类型时,性能也会显着下降;它们必须装入object中,然后从{{1}}中拆箱,这很慢,而且不安全。

事实上,你所建议的(嗯,仍然存在)存在!它被称为ArrayList并且非常可怕。

答案 1 :(得分:3)

来自MSDN

  

泛型允许您根据其所处理的精确数据类型定制方法,类,结构或接口。

当您将某些内容声明为object时,需要在执行特定于类型的操作之前将其强制转换为相应的数据类型。同样,对于带有派生类的示例,如果仅使用基类,则会失去使用派生类的属性的能力,从而使其毫无意义。

此外,在文档中,明确提到了一个优势:

  

创建泛型类的实例时,指定要替换类型参数的实际类型。这将建立一个新的泛型类,称为构造的泛型类,您选择的类型将替换为类型参数出现的所有位置。结果是一个类型安全的类,根据您选择的类型进行定制。

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

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

答案 2 :(得分:2)

因为您可以使用通用列表执行此操作:

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`
}

注意如何允许编译器知道列表的类型,这使我可以使用列表中的对象执行操作,否则需要进行强制转换。由于我在编译时知道我正在处理什么类型的对象,因此我可以在尝试执行某些操作之前发现错误,而不是在运行此代码之前不允许这样做。

答案 3 :(得分:2)

泛型提供了许多好处,如类型安全和避免装箱和拆箱。与使用对象相比,泛型更快,因为它避免了装箱和拆箱。有关收益的更多详细信息,请参阅this,如果您是新手,请访问以下链接:

An Introduction to C# Generics

Generics (C# Programming Guide)

<强>更新

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

相关问题