RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 622448
Accepted
Andrei Khotko
Andrei Khotko
Asked:2020-02-02 16:16:53 +0000 UTC2020-02-02 16:16:53 +0000 UTC 2020-02-02 16:16:53 +0000 UTC

C#。通过类型参数化和通过类型对象实现泛型类有什么区别?

  • 772

从性能的角度来看,我对这个问题很感兴趣。关于对象需要类型转换这一事实,我知道因此您可能会隐式出错。

更新。这个问题是在采访中被问到的。例如,我添加了 2 个类:

class SomeClass<T>
{
    private T[] storage;
}

class SomeClass
{
    private object[] storage;
}
c#
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    andreycha
    2020-02-02T16:32:20Z2020-02-02T16:32:20Z

    如果您使用值类型,您将获得额外的打包和后续解包开销。

    var numbers = new List<object>();
    numbers.Add(1); // упаковка №1
    numbers.Add(2); // упаковка №2
    int n1 = (int)numbers[0]; // распаковка №1
    int n2 = (int)numbers[1]; // распаковка №2
    

    好吧,正如您自己提到的,这种用法不是类型安全的。

    .NET 2.0 中引入了泛型集合,正是为了避免这些问题。早期版本使用ArrayList,大体上是List<object>.

    更新程序

    在您给出的示例中,一切都是一样的:如果storage将值类型的实例添加到数组中,则会发生打包。

    • 18
  2. Vadim Prokopchuk
    2020-02-02T19:31:47Z2020-02-02T19:31:47Z

    泛型代码相对于非泛型代码的优势:

    • 类型安全。当泛型算法应用于特定类型时,编译器和 CLR 理解这一点并确保在算法中只使用与该数据类型兼容的对象。尝试使用不兼容的对象将导致编译时错误或运行时异常。

      通过SomeType<Stream> some = new SomeType<Stream>()在您的变量中声明,storage可以仅存储该类型Stream或其派生对象。验证将在编写代码的阶段进行,即 将立即发出警告,而不是object- 在运行时。

    • 更简单易懂的代码。因为编译器强制执行类型安全,所以源代码需要更少的转换并且更容易编写和维护。
    • 生产力提高。在泛型出现之前,定义泛型算法的一种方法是定义其所有成员,以便它们“知道如何”使用 Object 数据类型。对于使用值类型实例的算法,CLR 必须在调用算法成员之前对实例进行装箱。打包需要在托管堆上分配内存,这会导致更频繁的垃圾回收,进而降低应用程序性能。因为可以编写通用算法来对特定值类型进行操作,所以可以按值传递值类型实例,而 CLR 不需要装箱。也不需要转换操作,因此 CLR 在转换它们时不需要控制类型安全,这也加快了代码速度。

    为了评估性能,一个非泛化的FCLArrayList类库和一个通用的.List

    方法Main- 调用两个测试

    public static void Main() {    
       ValueTypePerfTest();    
       ReferenceTypePerfTest();  
    }
    

    值类型测试:

     private static void ValueTypePerfTest() {    
        const Int32 count = 10000000;
    
        using (new OperationTimer("List<Int32>")) {      
            List<Int32> l = new List<Int32>();      
            for (Int32 n = 0; n < count; n++) {         
                l.Add(n);                 // Без упаковки         
                Int32 x = l[n];           // Без распаковки      
            }       
            l = null; // Для удаления в процессе уборки мусора   
        }
    
        using (new OperationTimer("ArrayList of Int32")) {      
            ArrayList a = new ArrayList();      
            for (Int32 n = 0; n < count; n++) {         
                a.Add(n);                  // Упаковка         
                Int32 x = (Int32) a[n];    // Распаковка      
            }       
            a = null; // Для удаления в процессе уборки мусора   
        } 
    }
    

    引用类型测试:

    private static void ReferenceTypePerfTest() {  
        const Int32 count = 10000000;
    
        using (new OperationTimer("List<String>")) {    
            List<String> l = new List<String>();    
            for (Int32 n = 0; n < count; n++) {       
                l.Add("X");                   // Копирование ссылки       
                String x = l[n];              // Копирование ссылки    
            }     
            l = null; // Для удаления в процессе уборки мусора  
        }
    
        using (new OperationTimer("ArrayList of String")) {      
            ArrayList a = new ArrayList();      
            for (Int32 n = 0; n < count; n++) {         
                a.Add("X");                 // Копирование ссылки         
                String x = (String) a[n];   // Проверка преобразования       
            }                             // и копирование ссылки       
            a = null; // Для удаления в процессе уборки мусора
        }
    }
    

    估计操作执行时间的类

    internal sealed class OperationTimer : IDisposable {  
        private Int64 m_startTime;  
        private String m_text;  
        private Int32 m_collectionCount;
    
        public OperationTimer(String text) {    
            PrepareForOperation();
            m_text = text;    
            m_collectionCount = GC.CollectionCount(0);
            // Эта команда должна быть последней в этом методе     
            // для максимально точной оценки быстродействия    
            m_startTime = Stopwatch.StartNew();  
        }
    
        public void Dispose() {     
            Console.WriteLine("{0} (GCs={1,3}) {2}", (m_stopwatch.Elapsed),        
                GC.CollectionCount(0), m_collectionCount, m_text);  
        }
    
        private static void PrepareForOperation() {    
            GC.Collect();    
            GC.WaitForPendingFinalizers();    
            GC.Collect();  
        } 
    } 
    

    试验结果

    00:00:01.6246959 (GCs=  6) List<Int32> 
    00:00:10.8555008 (GCs=390) ArrayList of Int32 
    00:00:02.5427847 (GCs=  4) List<String> 
    00:00:02.7944831 (GCs=  7) ArrayList of String
    

    结论

    使用 Int32,通用 List 算法比非通用 ArrayList 算法快得多。而且,差异巨大:1.6 秒对 11 秒,即快了7 倍!另外,在ArrayList算法中使用一个值类型(Int32)需要大量的装箱操作,因此需要390个垃圾回收程序,而在List算法中只有6个。

    引用类型的测试结果并不那么令人印象深刻:垃圾收集的时间和数量在这里大致相同。因此,在这种情况下,通用的 List 算法并没有真正的优势。但是,请记住,使用通用算法可以大大简化代码和编译时输入。


    资料来源: Jeffrey Richter“通过 C# 实现 CLR”

    • 7

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5