views:

79

answers:

3

OK OK, I know this is a hack, but this was for a tiny data-manipulation project and I wanted to play around. ;-)

I was always under the impression that the compiler would examine all anonymous types used in a C# program and if the properties were the same, it would only create one class behind the scenes.

So let's say I want to create an anonymous type out of some typed datasets that I have:

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

I can now do fun things like smallData2.Except(smallData1); etc., and it all works.

Now, what if I have a bigger pair of anonymous types:

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

Now when I do bigData2.Except(bigData1); the compiler complains:

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

Why? Too many properties, so the compiler decides it's not worth it to optimize?

Thanks!

+2  A: 

Have you tried

bigData2.Except(bigData1.AsQueryable());

I've just run a LINQ example with 40 properties and 20,000,000 rows and I don't run into an issue.

(Try this unfortunately not collapsible example in 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 ;}
}
Graphain
I imagine the problem is that AnonymousType#1 can't be cast to AnonymousType#2... the question is, why are they two different types to begin with?
BlueRaja - Danny Pflughoeft
+1  A: 

Yep. Its not the number of properties. How sure are you that your adapters are returning exactly the same data types?

Swanny
Yup, the adapters are returning slightly different data types, but the difference is only the kind of typed dataset (they come from different databases).All the properties are strings, which is why I figured I'd just use an anonymous type and that would let me treat the data as ONE kind of type instead of two.
Pandincus
OMG WHERE DID THAT DOUBLE COME FROM.Err ... okay, you were completely right. One of the types is indeed different. I guess that's what happens when you code late at night :-O
Pandincus
A: 

Anonymous types, like any type, are scoped to their containing assembly. The compiler can only treat as equal if the two adapters are in the same dll (actually, module IIRC).

Beyond that, I would check the types...

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