Skip to content

Commit 8dbd797

Browse files
author
rstam
committed
CSHARP-514: Changed ExpressionFormatter to introduce a platform neutral naming convention for anonymous types (along with some other minor improvements).
1 parent 43103bb commit 8dbd797

File tree

2 files changed

+34
-29
lines changed

2 files changed

+34
-29
lines changed

Driver/Linq/Expressions/ExpressionFormatter.cs

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System.Collections.ObjectModel;
1919
using System.Linq;
2020
using System.Linq.Expressions;
21+
using System.Runtime.CompilerServices;
2122
using System.Text;
2223
using System.Text.RegularExpressions;
2324

@@ -132,7 +133,7 @@ protected override Expression VisitConstant(ConstantExpression node)
132133
// need to check node.Type instead of value.GetType() because boxed Nullable<T> values are boxed as <T>
133134
if (node.Type.IsGenericType && node.Type.GetGenericTypeDefinition() == typeof(Nullable<>))
134135
{
135-
_sb.AppendFormat("({0})", FriendlyClassName(node.Type));
136+
_sb.AppendFormat("({0})", FriendlyTypeName(node.Type));
136137
}
137138
VisitValue(node.Value);
138139
return node;
@@ -179,7 +180,7 @@ protected override Expression VisitInvocation(InvocationExpression node)
179180
protected override Expression VisitLambda(LambdaExpression node)
180181
{
181182
_sb.Append("(");
182-
_sb.Append(string.Join(", ", node.Parameters.Select(p => p.Type.Name + " " + p.Name).ToArray()));
183+
_sb.Append(string.Join(", ", node.Parameters.Select(p => FriendlyTypeName(p.Type) + " " + p.Name).ToArray()));
183184
_sb.Append(") => ");
184185
Visit(node.Body);
185186
return node;
@@ -284,7 +285,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
284285
{
285286
if (node.Method.IsStatic)
286287
{
287-
_sb.Append(node.Method.DeclaringType.Name);
288+
_sb.Append(FriendlyTypeName(node.Method.DeclaringType));
288289
}
289290
else
290291
{
@@ -294,7 +295,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
294295
_sb.Append(node.Method.Name);
295296
if (node.Method.IsGenericMethod)
296297
{
297-
_sb.AppendFormat("<{0}>", string.Join(", ", node.Method.GetGenericArguments().Select(t => FriendlyClassName(t)).ToArray()));
298+
_sb.AppendFormat("<{0}>", string.Join(", ", node.Method.GetGenericArguments().Select(t => FriendlyTypeName(t)).ToArray()));
298299
}
299300
_sb.Append("(");
300301
var separator = "";
@@ -316,7 +317,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
316317
protected override NewExpression VisitNew(NewExpression node)
317318
{
318319
_sb.Append("new ");
319-
_sb.Append(node.Type.Name);
320+
_sb.Append(FriendlyTypeName(node.Type));
320321
_sb.Append("(");
321322
var separator = "";
322323
foreach (var arg in node.Arguments)
@@ -337,7 +338,7 @@ protected override NewExpression VisitNew(NewExpression node)
337338
protected override Expression VisitNewArray(NewArrayExpression node)
338339
{
339340
var elementType = node.Type.GetElementType();
340-
_sb.AppendFormat("new {0}[] {{ ", PublicClassName(elementType));
341+
_sb.AppendFormat("new {0}[] {{ ", FriendlyTypeName(elementType));
341342
var separator = "";
342343
foreach (var item in node.Expressions)
343344
{
@@ -370,7 +371,7 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression node)
370371
_sb.Append("(");
371372
Visit(node.Expression);
372373
_sb.Append(" is ");
373-
_sb.Append(FriendlyClassName(node.TypeOperand));
374+
_sb.Append(FriendlyTypeName(node.TypeOperand));
374375
_sb.Append(")");
375376
return node;
376377
}
@@ -385,7 +386,7 @@ protected override Expression VisitUnary(UnaryExpression node)
385386
switch (node.NodeType)
386387
{
387388
case ExpressionType.ArrayLength: break;
388-
case ExpressionType.Convert: _sb.AppendFormat("({0})", FriendlyClassName(node.Type)); break;
389+
case ExpressionType.Convert: _sb.AppendFormat("({0})", FriendlyTypeName(node.Type)); break;
389390
case ExpressionType.Negate: _sb.Append("-"); break;
390391
case ExpressionType.Not: _sb.Append("!"); break;
391392
case ExpressionType.Quote: break;
@@ -401,31 +402,35 @@ protected override Expression VisitUnary(UnaryExpression node)
401402
}
402403

403404
// private methods
404-
private string FriendlyClassName(Type type)
405+
private string FriendlyTypeName(Type type)
405406
{
406-
if (!type.IsGenericType)
407+
var typeName = IsAnonymousType(type) ? "__AnonymousType" : type.Name;
408+
409+
if (type.IsGenericType)
407410
{
408-
return type.Name;
411+
var sb = new StringBuilder();
412+
sb.AppendFormat("{0}<", Regex.Replace(typeName, @"\`\d+$", ""));
413+
foreach (var typeParameter in type.GetGenericArguments())
414+
{
415+
sb.AppendFormat("{0}, ", FriendlyTypeName(typeParameter));
416+
}
417+
sb.Remove(sb.Length - 2, 2);
418+
sb.Append(">");
419+
return sb.ToString();
409420
}
410-
411-
var sb = new StringBuilder();
412-
sb.AppendFormat("{0}<", Regex.Replace(type.Name, @"\`\d+$", ""));
413-
foreach (var typeParameter in type.GetGenericArguments())
421+
else
414422
{
415-
sb.AppendFormat("{0}, ", FriendlyClassName(typeParameter));
423+
return typeName;
416424
}
417-
sb.Remove(sb.Length - 2, 2);
418-
sb.Append(">");
419-
return sb.ToString();
420425
}
421426

422-
private string PublicClassName(Type type)
427+
private bool IsAnonymousType(Type type)
423428
{
424-
while (!type.IsPublic)
425-
{
426-
type = type.BaseType;
427-
}
428-
return FriendlyClassName(type);
429+
// don't test for too many things in case implementation details change in the future
430+
return
431+
Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) &&
432+
type.IsGenericType &&
433+
type.Name.Contains("Anon"); // don't check for more than "Anon" so it works in mono also
429434
}
430435

431436
private void VisitValue(object value)
@@ -440,7 +445,7 @@ private void VisitValue(object value)
440445
if (a != null && a.Rank == 1)
441446
{
442447
var elementType = a.GetType().GetElementType();
443-
_sb.AppendFormat("{0}[]:{{", elementType.Name);
448+
_sb.AppendFormat("{0}[]:{{", FriendlyTypeName(elementType));
444449
var separator = " ";
445450
foreach (var item in a)
446451
{
@@ -487,7 +492,7 @@ private void VisitValue(object value)
487492
var e = value as Enum;
488493
if (e != null)
489494
{
490-
_sb.Append(e.GetType().Name + "." + e.ToString());
495+
_sb.Append(FriendlyTypeName(e.GetType()) + "." + e.ToString());
491496
return;
492497
}
493498

@@ -527,7 +532,7 @@ private void VisitValue(object value)
527532
var type = value as Type;
528533
if (type != null)
529534
{
530-
_sb.AppendFormat("typeof({0})", FriendlyClassName(type));
535+
_sb.AppendFormat("typeof({0})", FriendlyTypeName(type));
531536
return;
532537
}
533538

DriverUnitTests/Linq/SelectOfTypeTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public void TestOfTypeDWithProjection()
166166
Assert.AreEqual(typeof(D), selectQuery.OfType);
167167
Assert.IsNull(selectQuery.OrderBy);
168168
Assert.IsNotNull(selectQuery.Projection);
169-
Assert.AreEqual("(D x) => new <>f__AnonymousType1`1(x.d)", ExpressionFormatter.ToString(selectQuery.Projection));
169+
Assert.AreEqual("(D x) => new __AnonymousType<Int32>(x.d)", ExpressionFormatter.ToString(selectQuery.Projection));
170170
Assert.IsNull(selectQuery.Skip);
171171
Assert.IsNull(selectQuery.Take);
172172

0 commit comments

Comments
 (0)