Can anyone tell me why these two anonymous types are not the same?
{Name = "<>f__AnonymousType0`6" FullName = "<>f__AnonymousType0`6[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Decimal, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
{Name = "<>f__AnonymousType0#1`6" FullName = "<>f__AnonymousType0#1`6[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Decimal, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
CLARIFICATION: (I don't know if it's important but one anonymous type has a # symbol. oh, #01)
I have these two pieces of code. In the same class (assembly). One returns one of the anonymous types above and the other return the other one. However, both are evaluating the same expression.
I've done the comparisons below (some just to see the result even though some should correctly return false). But, they all return false. (I may have left out some that I've tried)
ms returns one of the anonymous types. resultOfSelect returns the other anonymous type.
Note: methodArgumentStringContainingSelectStatement in second piece of code contains the same expression as a string.
'ms == resultOfSelect'
'ms.GetType() == resultOfSelect'
'ms.GetType().Equals(resultOfSelect.GetType())'
'ms.GetType() == resultOfSelect.GetType()'
resultOfSelect debugView is:
{Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<<>f__AnonymousType0#1>}
ms debugView is:
{Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<<>f__AnonymousType0>}
1st statement:
' var ms = (instanceContainingSelectMethod as IQueryable<Emar>).Select( nextEmarClassInstanceFromPreviousSelectMany => new { Schedule = nextEmarClassInstanceFromPreviousSelectMany.BEmem.Category.Substring(0, 1), Category = "0" + nextEmarClassInstanceFromPreviousSelectMany.BEmem.Category.Substring(1), Description = "", Year = nextEmarClassInstanceFromPreviousSelectMany.Month.Year, Time = nextEmarClassInstanceFromPreviousSelectMany.Actual_Time, Units = ((nextEmarClassInstanceFromPreviousSelectMany.BEmem.PurchDate ?? nextEmarClassInstanceFromPreviousSelectMany.BEmem.InServiceDate).Value.Year == nextEmarClassInstanceFromPreviousSelectMany.Month.Year)
? (nextEmarClassInstanceFromPreviousSelectMany.BEmem.SoldDate != null)
? (nextEmarClassInstanceFromPreviousSelectMany.BEmem.SoldDate.Value.Year == nextEmarClassInstanceFromPreviousSelectMany.Month.Year)
? (((12d - ((double)((nextEmarClassInstanceFromPreviousSelectMany.BEmem.PurchDate != null)
? (System.DateTime?)nextEmarClassInstanceFromPreviousSelectMany.BEmem.PurchDate.Value
: nextEmarClassInstanceFromPreviousSelectMany.BEmem.InServiceDate).Value.Month)) + 1d) - (12d - ((12d - ((double)nextEmarClassInstanceFromPreviousSelectMany.BEmem.SoldDate.Value.Month)) + 1d))) / 12d
: ((12d - ((double)(nextEmarClassInstanceFromPreviousSelectMany.BEmem.PurchDate ?? nextEmarClassInstanceFromPreviousSelectMany.BEmem.InServiceDate).Value.Month)) + 1d) / 12d
: ((12d - ((double)(nextEmarClassInstanceFromPreviousSelectMany.BEmem.PurchDate ?? nextEmarClassInstanceFromPreviousSelectMany.BEmem.InServiceDate).Value.Month)) + 1d) / 12d
: (nextEmarClassInstanceFromPreviousSelectMany.BEmem.SoldDate != null)
? (nextEmarClassInstanceFromPreviousSelectMany.BEmem.SoldDate.Value.Year == nextEmarClassInstanceFromPreviousSelectMany.Month.Year)
? (12d - ((12d - ((double)nextEmarClassInstanceFromPreviousSelectMany.BEmem.SoldDate.Value.Month)) + 1d)) / 12d
: 1d
: 1d } );'
2nd statement:
'Func<IQueryable<Emar>, object> customSelectManyDelegate =
await CSharpScript
.EvaluateAsync<Func<IQueryable<Emar>, object>>(methodArgumentStringContainingSelectStatement, options);
resultOfSelect = customSelectManyDelegate(instanceContainingSelectMethod as IQueryable<Emar>);'
Anonymous types are really only meant to be used in the context of a single method, or maybe in some reflection scenarios where just the shape is important, not the type.
So: asking why the type is different is already breaking the intent of the language feature. As for why they are different: probably different assemblies or modules. This applies in particular in web projects where the views are compiled later than the main code.
But: don't rely on this equality, basically. It isn't guaranteed. If you care what the type is (and what it is equal to), then your options include:
ValueTuple<...>
, but with first-class language support including pseudo-names that propagate from callee to caller, but not from caller to callee)Tuple<...>
)Using anonymous types doesn't really appear in the list.