【问题标题】:Compiler optimizations of anonymous types匿名类型的编译器优化
【发布时间】:2011-03-19 21:13:35
【问题描述】:

好的好的,我知道这是一个 hack,但这是针对一个小型数据操作项目,我想尝试一下。 ;-)

我一直认为编译器会检查 C# 程序中使用的所有匿名类型,如果属性相同,它只会在后台创建一个类。

假设我想从我拥有的一些类型化数据集中创建一个匿名类型:

var smallData1 = new smallData1().GetData().Select(
    x => new { Name = x.NAME, x.ADDRESS, City = x.CITY, State = x.STATE,
    Zip = x.ZIP, Country = x.COUNTRY, ManagerName = x.MANAGER_NAME,
    ManagerID = x.MANAGER_ID });

var smallData2 = new smallData2().GetData().Select(
    x => new { x.Name, x.ADDRESS, x.City, x.State, x.Zip, x.Country,
    x.ManagerName,x.ManagerID });

我现在可以做 smallData2.Except(smallData1); 等有趣的事情,而且一切正常。

现在,如果我有一对更大的匿名类型怎么办:

var bigData1 = new BigAdapter1().GetData().Select(
    x => new { x.FirstName, x.LastName, x.Address, x.City, x.State,
    x.Zip, x.Country, x.Phone, x.Email, x.Website, x.Custom1, x.Custom2,
    x.Custom3, x.Custom4, x.Custom5, x.Custom6, x.Custom7, x.Custom8, x.Custom9,
    x.Custom10, x.Custom11, x.Custom12, x.Custom13, x.Custom14, x.Custom15,
    x.Custom16, x.Custom17, x.Custom18, x.Custom19, x.Custom20, x.Custom21,
    x.Custom22, x.Custom23, x.Custom24, x.Custom25, x.Custom26, x.Custom27,
    x.Custom28, x.Custom29});

var bigData2 = new BigAdapter2().GetData().Select(
    x => new { x.FirstName, x.LastName, x.Address, x.City, x.State, x.Zip,
    x.Country, x.Phone, x.Email, x.Website, x.Custom1, x.Custom2, x.Custom3,
    x.Custom4, x.Custom5, x.Custom6, x.Custom7, x.Custom8, x.Custom9, x.Custom10,
    x.Custom11, x.Custom12, x.Custom13, x.Custom14, x.Custom15, x.Custom16,
    x.Custom17, x.Custom18, x.Custom19, x.Custom20, x.Custom21, x.Custom22,
    x.Custom23, x.Custom24, x.Custom25, x.Custom26, x.Custom27,
    x.Custom28, x.Custom29});

现在,当我执行 bigData2.Except(bigData1); 时,编译器会抱怨:

Instance argument: cannot convert from
'System.Data.EnumerableRowCollection<AnonymousType#1>' to
'System.Linq.IQueryable<AnonymousType#2>'

为什么?属性太多,编译器认为不值得优化?

谢谢!

【问题讨论】:

    标签: c# .net-4.0 anonymous-types compiler-optimization


    【解决方案1】:

    是的。它不是属性的数量。您有多确定您的适配器返回完全相同的数据类型?

    【讨论】:

    • 是的,适配器返回的数据类型略有不同,但区别仅在于类型化数据集的类型(它们来自不同的数据库)。所有的属性都是字符串,这就是为什么我想我只使用匿名类型,这样我就可以将数据视为一种类型而不是两种类型。
    • 天哪,这双是从哪里来的。呃……好吧,你完全正确。其中一种类型确实不同。我想这就是你在深夜编码时会发生的事情:-O
    【解决方案2】:

    你试过了吗

    bigData2.Except(bigData1.AsQueryable());
    

    我刚刚运行了一个包含 40 个属性和 20,000,000 行的 LINQ 示例,我没有遇到任何问题。

    (不幸的是,在 LINQPad 中试试这个不可折叠的示例)

    void Main()
    {
     Test t = new Test();
     var a = Enumerable.Range(1,10000000).Select(i => new  
     { 
      t.T0, t.T1, t.T2, t.T3, t.T4, t.T5, t.T6, t.T7, t.T8, t.T9,
      t.T10, t.T11, t.T12, t.T13, t.T14, t.T15, t.T16, t.T17, t.T18, t.T19,
      t.T20, t.T21, t.T22, t.T23, t.T24, t.T25, t.T26, t.T27, t.T28, t.T29,
      t.T30, t.T31, t.T32, t.T33, t.T34, t.T35, t.T36, t.T37, t.T38, t.T39,
     });
    
     Test2 t2 = new Test2();
     var b = Enumerable.Range(1,10000000).Select(i => new  
     { 
      t2.T0, t2.T1, t2.T2, t2.T3, t2.T4, t2.T5, t.T6, t2.T7, t2.T8, t2.T9,
      t2.T10, t2.T11, t2.T12, t2.T13, t2.T14, t2.T15, t2.T16, t2.T17, t2.T18, t2.T19,
      t2.T20, t2.T21, t2.T22, t2.T23, t2.T24, t2.T25, t2.T26, t2.T27, t2.T28, t2.T29,
      t2.T30, t2.T31, t2.T32, t2.T33, t2.T34, t2.T35, t2.T36, t2.T37, t2.T38, t2.T39,
     });
    
     a.Except(b).Dump();
    }
    
    class Test
    {
     public string T0 { get; set ;}
     public string T1 { get; set ;}
     public string T2 { get; set ;}
     public string T3 { get; set ;}
     public string T4 { get; set ;}
     public string T5 { get; set ;}
     public string T6 { get; set ;}
     public string T7 { get; set ;}
     public string T8 { get; set ;}
     public string T9 { get; set ;}
     public string T10 { get; set ;}
     public string T11 { get; set ;}
     public string T12 { get; set ;}
     public string T13 { get; set ;}
     public string T14 { get; set ;}
     public string T15 { get; set ;}
     public string T16 { get; set ;}
     public string T17 { get; set ;}
     public string T18 { get; set ;}
     public string T19 { get; set ;}
     public string T20 { get; set ;}
     public string T21 { get; set ;}
     public string T22 { get; set ;}
     public string T23 { get; set ;}
     public string T24 { get; set ;}
     public string T25 { get; set ;}
     public string T26 { get; set ;}
     public string T27 { get; set ;}
     public string T28 { get; set ;}
     public string T29 { get; set ;}
     public string T30 { get; set ;}
     public string T31 { get; set ;}
     public string T32 { get; set ;}
     public string T33 { get; set ;}
     public string T34 { get; set ;}
     public string T35 { get; set ;}
     public string T36 { get; set ;}
     public string T37 { get; set ;}
     public string T38 { get; set ;}
     public string T39 { get; set ;}
    }
    
    class Test2
    {
     public string T0 { get; set ;}
     public string T1 { get; set ;}
     public string T2 { get; set ;}
     public string T3 { get; set ;}
     public string T4 { get; set ;}
     public string T5 { get; set ;}
     public string T6 { get; set ;}
     public string T7 { get; set ;}
     public string T8 { get; set ;}
     public string T9 { get; set ;}
     public string T10 { get; set ;}
     public string T11 { get; set ;}
     public string T12 { get; set ;}
     public string T13 { get; set ;}
     public string T14 { get; set ;}
     public string T15 { get; set ;}
     public string T16 { get; set ;}
     public string T17 { get; set ;}
     public string T18 { get; set ;}
     public string T19 { get; set ;}
     public string T20 { get; set ;}
     public string T21 { get; set ;}
     public string T22 { get; set ;}
     public string T23 { get; set ;}
     public string T24 { get; set ;}
     public string T25 { get; set ;}
     public string T26 { get; set ;}
     public string T27 { get; set ;}
     public string T28 { get; set ;}
     public string T29 { get; set ;}
     public string T30 { get; set ;}
     public string T31 { get; set ;}
     public string T32 { get; set ;}
     public string T33 { get; set ;}
     public string T34 { get; set ;}
     public string T35 { get; set ;}
     public string T36 { get; set ;}
     public string T37 { get; set ;}
     public string T38 { get; set ;}
     public string T39 { get; set ;}
    }
    

    【讨论】:

    • 我想问题是 AnonymousType#1 不能转换为 AnonymousType#2 ......问题是,为什么它们是两种不同的类型?
    【解决方案3】:

    与任何类型一样,匿名类型仅限于其包含的程序集。如果两个适配器在同一个 dll 中(实际上是模块 IIRC),编译器只能视为相等。

    除此之外,我会检查类型...

    static Type Identify<T>(IEnumerable<T>) {return typeof(T);}
    ...
    var t1= Identify(bigData1), t2= Identify(bigData2);
    if(t1 == t2) {
        Console.WriteLine("they're the same");
    } else {
        var props1 = t1.GetProperties(), props2 = t2.GetProperties();
        if(props1.Length != props2.Length) {
            Console.WriteLine(props1.Length + " vs " + props2.Length);
        } else {
            Array.Sort(props1, p => p.Name);
            Array.Sort(props2, p => p.Name);
            for(int i = 0 ; i < props1.Length ; i++) {
                if(props1[i].Name != props2[i].Name)
                    Console.WriteLine(props1[i].Name + " vs " + props2[i].Name);
                if(props1[i].PropertyType != props2[i].PropertyType)
                    Console.WriteLine(props1[i].PropertyType + " vs " + props2[i].PropertyType );
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-08
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多