• 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

  • 相关阅读:
    el-select下拉框选项太多导致卡顿,使用下拉框分页来解决
    vue+elementui前端添加数字千位分割
    Failed to check/redeclare auto-delete queue(s)
    周末啦,做几道面试题放松放松吧!
    idea快捷键
    解决flink运行过程中报错Could not allocate enough slots within timeout of 300000 ms to run the job. Please make sure that the cluster has enough resources.
    用.net平台实现websocket server
    MQTT实战3
    Oracle 查看当前用户下库里所有的表、存储过程、触发器、视图
    idea从svn拉取项目不识别svn
  • 原文地址:https://www.cnblogs.com/ming11/p/4542197.html
Copyright © 2020-2023  润新知