zoukankan      html  css  js  c++  java
  • C# 泛型

    一、前言

    首先谈谈泛型,包括Java, C++都有自己的泛型(模版),这种机制大大的减少了代码的数量,是一种类型的抽象。集合就我了解C++的 STL 中的vector<T>, list<T>, map<T,T> 等, .net 中的List<T>, HashTable<T,T>等,都是对基本数据结构的实现,如链表,队列,栈,等。但是在具体使用中,不同的语言,如果使用不当,我造成严重的性能影响,合格的程序员应该了解这些性能陷阱。

    二、.net 泛型

    在.net中通过使用泛型,我们可以达到以下两个目的:

    1.Type safe  2. No Boxing.

    这个比较好理解,举个例子ArrayList, 其源码如下:

    public class ArrayList : IEnumerable, ICollection, IList
        {
            private object[] items;
            private int size;
            public ArrayList(int initalCapacity)
            {
                items = new object[initalCapacity];
            }
    
            public void Add(object item)
            {
                if (size < items.Length - 1)
                {
                    items[size] = item;
                    ++size;
                }
                else {
                    //Allocate a larger array, copy the elements to there.
                }
            }
    
            public object this[int index]
            {
                get
                {
                    if (index < 0 || index >= size) throw new IndexOutOfRangeException();
                    return items[index];
                }
                set
                {
                    if (index < 0 || index >= size) throw new IndexOutOfRangeException();
                }
            }
    
            // ommit other details
    
        }

    可见在Add的时候会有装箱操作发生,如果存放的是1,000,000的Point, 将会有大量的内存被浪费掉(8M (extra)+ 8M(data) + 4M(reference)), 除了因为装箱引起内存浪费外,因为我们相关的操作时基于System.Object,类型安全也是一个大问题。

    泛型可以完美的解决这个问题,原理看简化的源码:

    public class List<T> : IEnumerable<T>, ICollection<T>, IList<T>
        {
            private T[] items;
            private int size;
            public List(int initalCapacity)
            {
                // does this work?
                items = new T[initalCapacity];
            }
    
            public void Add(T item)
            {
                if (size < items.Length - 1)
                {
                    items[size] = item;
                    ++size;
                }
                else
                {
                    //Allocate a larger array, copy the elements to there.
                }
            }
    
            public T this[int index]
            {
                get
                {
                    if (index < 0 || index >= size) throw new IndexOutOfRangeException();
                    return items[index];
                }
                set
                {
                    if (index < 0 || index >= size) throw new IndexOutOfRangeException();
                    items[index] = value;
                }
            }
    
            // ommit other details
    
        }

    不用担心类型安全和装箱拆箱的问题了。但是如果我增加一些比较的功能呢?

     public static int BinarySearch<T>(T[] array, T element) {

    //At some point in the algorithm, we need to compare:

    if (array[x] < array[y]) {

    ...

    }

    System.Object没有实现 static operator <, 对于上面这个函数,大部分都会类型都会编译错误。我们可以使用模版函数的限制功能,来保证T实现了 比较的 , .NET一种有5种限制:

    public class Widget{
            public void Display(int i, int j) { }
        }
    
        public class GenericDemo
        {
            // T must implement an interface
            public string Format<T>(T instance) where T: IFormattable
            {
                return instance.ToString("N", CultureInfo.CurrentCulture);
                // OK, T must have IFormattable.ToString(...)
            }
    
            // T must based on a base class
            public void Display<T>(T widget) where T : Widget
            {
                widget.Display(1, 2);
            }
    
            // T must a parameterless cosntructor
            public T Create<T>() where T : new()
            {
                return new T();
            }
    
            // T must be a reference type:
            public void ReferencesOnly<T>(T reference) where T : class { }
    
            // T must be a value type:
            public void ValueType<T>(T valueType) where T : struct { }
    
     
    }

    这样我们可以这样写BinarySearch了: 

    public static int BinarySearch<T>(T[] array, T element) where T : IComparable<T> {

            //At some point in the algorithm, we need to compare:

                 int x = 1; int y = 2;

            if (array[x].CompareTo(array[y]) < 0) {

            //...

            }

            }

    接下来我们再讨论 IEquatable<T>,先看下面这个函数:

    public static void CallEquals<T>(T instance) {
    instance.Equals(instance);
    }

    Equals将会调用基类的虚函数Equals,它的参数是System.Object,会产生装箱。但是我们实现了IEquatable<T>,使用在模版限定中, 就可以避免装箱了

    //From the .NET Framework:
    public interface IEquatable<T> {
    bool Equals(T other);
    }
    
    public static void CallEquals<T>(T instance) where T : IEquatable<T> {
    instance.Equals(instance);
    }

    这个函数将不再调用虚函数的Equals, 这样就避免了装箱。我们在前一篇文章中,提到valueType的最佳实践中,要现实IEquatable<T> 就是这个原因。 那么按理说所有的集合最好都限制为IEquatable<T>类型,但是为了扩展性的考虑,我们用组合的形式,委托给GenericEqualityComparer, 举个例子List<T>.Contains, 前看简化源码:

    public bool Contains(T item)
            {
                if (item == null)
                {
                    for (int i = 0; i < this._size; i++)
                    {
                        if (this._items[i] == null)
                        {
                            return true;
                        }
                    }
                    return false;
                }
                EqualityComparer<T> @default = EqualityComparer<T>.Default;
                for (int j = 0; j < this._size; j++)
                {
                    if (@default.Equals(this._items[j], item))
                    {
                        return true;
                    }
                }
                return false;
            }

    把比较委托给了EqualityComaprer<T>, 如果我们替换了它,就可以改变我们的比较策略了。除了equals,这里还有别的实现数学的泛型知识。请看参考链接

    三、参考

    <<Pro .NET Performance>>

    http://www.codeproject.com/Articles/8531/Using-generics-for-calculations

  • 相关阅读:
    自定义promise的实现
    数据双向邦定1
    上线遇到的bug
    UEGrids.js
    staticFileServer.js
    Promise
    响应式布局实例
    悬浮框的兼容性
    Fiddler Web Debugger
    js根据当前日期提前N天或推后N天的方法
  • 原文地址:https://www.cnblogs.com/ming11/p/4542197.html
Copyright © 2011-2022 走看看