Skip to content

Commit 0b04946

Browse files
authored
Getter and setter call syntax (#38)
* Allow customization of ConstantExpression and fix method calls to property getters/setters (#36) * add convert property methods setting * implement ConvertPropertyMethodsToSimpleSyntax * add setting "NameConstantsUsing" * allow user to customize how a ConstantExpression is written * Adding public instance getter and setter call translation tests Making property method call to property syntax translation the default Tidying * Support for static property getter and setter call translation * Test coverage for non-public property getter and setter call translation * Test coverage for use-defined constant translator * Updating Readme
1 parent 8d4d6b8 commit 0b04946

File tree

7 files changed

+304
-16
lines changed

7 files changed

+304
-16
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ To include namespaces when outputting Type names, use:
2626
To define a custom factory for naming anonymous types, use:
2727

2828
string readable = myExpression
29-
.ToReadableString(c => c.NameAnonymousTypesUsing(anonType => GetAnonTypeName(anonType)));
29+
.ToReadableString(c => c.NameAnonymousTypesUsing(
30+
anonType => GetAnonTypeName(anonType)));
31+
32+
To define a custom factory for translating ConstantExpression values, use:
33+
34+
string readable = myExpression
35+
.ToReadableString(c => c.TranslateConstantsUsing(
36+
(constantType, constantValue) => GetConstantValue(constantType, constantValue)));
3037

3138
To output a source code comment when a lambda is '[quoted](https://stackoverflow.com/questions/3716492/what-does-expression-quote-do-that-expression-constant-can-t-already-do)', use:
3239

ReadableExpressions.UnitTests/WhenTranslatingConstants.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,31 @@ public void ShouldTranslateALambdaConstant()
429429

430430
translated.ShouldBe(EXPECTED.TrimStart());
431431
}
432+
433+
// See https://github.com/agileobjects/ReadableExpressions/issues/35
434+
[Fact]
435+
public void ShouldUseAUserDefinedConstantTranslator()
436+
{
437+
var stringConstant = Expression.Constant("hello!", typeof(string));
438+
439+
var translated = ToReadableString(
440+
stringConstant,
441+
settings => settings.TranslateConstantsUsing((t, v) => "custom!"));
442+
443+
translated.ShouldBe("custom!");
444+
}
445+
446+
[Fact]
447+
public void ShouldUseADefaultValueIfUserDefinedConstantTranslatorReturnsNull()
448+
{
449+
var stringConstant = Expression.Constant("hello!", typeof(string));
450+
451+
var translated = ToReadableString(
452+
stringConstant,
453+
settings => settings.TranslateConstantsUsing((t, v) => null));
454+
455+
translated.ShouldBe("null");
456+
}
432457
}
433458

434459
// ReSharper disable UnusedTypeParameter

ReadableExpressions.UnitTests/WhenTranslatingMemberAccesses.cs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,139 @@ public void ShouldTranslateAStaticMemberExpressionUsingTheDeclaringType()
139139
translated.ShouldBe("IndexedProperty.Default");
140140
}
141141

142+
// See https://github.com/agileobjects/ReadableExpressions/issues/35
143+
[Fact]
144+
public void ShouldTranslateAnInstancePropertyGetterCall()
145+
{
146+
var publicInstanceGetter = typeof(PropertiesHelper)
147+
.GetPublicInstanceProperty(nameof(PropertiesHelper.PublicInstance))
148+
.GetGetter();
149+
150+
publicInstanceGetter.ShouldNotBeNull();
151+
152+
var variable = Expression.Variable(typeof(PropertiesHelper), "helper");
153+
var getterAccess = Expression.Call(variable, publicInstanceGetter);
154+
155+
var translated = ToReadableString(getterAccess);
156+
157+
translated.ShouldBe("helper.PublicInstance");
158+
}
159+
160+
[Fact]
161+
public void ShouldTranslateAnInstancePropertySetterCall()
162+
{
163+
var publicInstanceSetter = typeof(PropertiesHelper)
164+
.GetPublicInstanceProperty(nameof(PropertiesHelper.PublicInstance))
165+
.GetSetter();
166+
167+
publicInstanceSetter.ShouldNotBeNull();
168+
169+
var variable = Expression.Variable(typeof(PropertiesHelper), "helper");
170+
var setterCall = Expression.Call(variable, publicInstanceSetter, Expression.Constant(123));
171+
172+
var translated = ToReadableString(setterCall);
173+
174+
translated.ShouldBe("helper.PublicInstance = 123");
175+
}
176+
177+
[Fact]
178+
public void ShouldTranslateAStaticPropertyGetterCall()
179+
{
180+
var publicStaticGetter = typeof(PropertiesHelper)
181+
.GetPublicStaticProperty(nameof(PropertiesHelper.PublicStatic))
182+
.GetGetter();
183+
184+
publicStaticGetter.ShouldNotBeNull();
185+
186+
var getterAccess = Expression.Call(publicStaticGetter);
187+
188+
var translated = ToReadableString(getterAccess);
189+
190+
translated.ShouldBe("PropertiesHelper.PublicStatic");
191+
}
192+
193+
[Fact]
194+
public void ShouldTranslateAStaticPropertySetterCall()
195+
{
196+
var publicStaticSetter = typeof(PropertiesHelper)
197+
.GetPublicStaticProperty(nameof(PropertiesHelper.PublicStatic))
198+
.GetSetter();
199+
200+
publicStaticSetter.ShouldNotBeNull();
201+
202+
var setterCall = Expression.Call(publicStaticSetter, Expression.Constant(456));
203+
204+
var translated = ToReadableString(setterCall);
205+
206+
translated.ShouldBe("PropertiesHelper.PublicStatic = 456");
207+
}
208+
209+
[Fact]
210+
public void ShouldTranslateANonPublicInstancePropertyGetterCall()
211+
{
212+
var publicInstanceGetter = typeof(PropertiesHelper)
213+
.GetNonPublicInstanceProperty(nameof(PropertiesHelper.NonPublicInstance))
214+
.GetGetter(nonPublic: true);
215+
216+
publicInstanceGetter.ShouldNotBeNull();
217+
218+
var variable = Expression.Variable(typeof(PropertiesHelper), "helper");
219+
var getterAccess = Expression.Call(variable, publicInstanceGetter);
220+
221+
var translated = ToReadableString(getterAccess);
222+
223+
translated.ShouldBe("helper.NonPublicInstance");
224+
}
225+
226+
[Fact]
227+
public void ShouldTranslateANonPublicInstancePropertySetterCall()
228+
{
229+
var nonPublicInstanceSetter = typeof(PropertiesHelper)
230+
.GetNonPublicInstanceProperty(nameof(PropertiesHelper.NonPublicInstance))
231+
.GetSetter(nonPublic: true);
232+
233+
nonPublicInstanceSetter.ShouldNotBeNull();
234+
235+
var variable = Expression.Variable(typeof(PropertiesHelper), "helper");
236+
var setterCall = Expression.Call(variable, nonPublicInstanceSetter, Expression.Constant(123));
237+
238+
var translated = ToReadableString(setterCall);
239+
240+
translated.ShouldBe("helper.NonPublicInstance = 123");
241+
}
242+
243+
[Fact]
244+
public void ShouldTranslateANonPublicStaticPropertyGetterCall()
245+
{
246+
var nonPublicStaticGetter = typeof(PropertiesHelper)
247+
.GetNonPublicStaticProperty(nameof(PropertiesHelper.NonPublicStatic))
248+
.GetGetter(nonPublic: true);
249+
250+
nonPublicStaticGetter.ShouldNotBeNull();
251+
252+
var getterAccess = Expression.Call(nonPublicStaticGetter);
253+
254+
var translated = ToReadableString(getterAccess);
255+
256+
translated.ShouldBe("PropertiesHelper.NonPublicStatic");
257+
}
258+
259+
[Fact]
260+
public void ShouldTranslateANonPublicStaticPropertySetterCall()
261+
{
262+
var nonPublicStaticSetter = typeof(PropertiesHelper)
263+
.GetNonPublicStaticProperty(nameof(PropertiesHelper.NonPublicStatic))
264+
.GetSetter(nonPublic: true);
265+
266+
nonPublicStaticSetter.ShouldNotBeNull();
267+
268+
var setterCall = Expression.Call(nonPublicStaticSetter, Expression.Constant(456));
269+
270+
var translated = ToReadableString(setterCall);
271+
272+
translated.ShouldBe("PropertiesHelper.NonPublicStatic = 456");
273+
}
274+
142275
[Fact]
143276
public void ShouldTranslateAParamsArrayArgument()
144277
{
@@ -462,6 +595,17 @@ public void ShouldTranslateExplicitMethodOperatorUse()
462595

463596
#region Helper Classes
464597

598+
internal class PropertiesHelper
599+
{
600+
public static int PublicStatic { get; set; }
601+
602+
public int PublicInstance { get; set; }
603+
604+
internal static int NonPublicStatic { get; set; }
605+
606+
internal int NonPublicInstance { get; set; }
607+
}
608+
465609
internal class IndexedProperty
466610
{
467611
public static readonly object Default = new IndexedProperty(new object[0]);

ReadableExpressions/TranslationSettings.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,30 @@ public TranslationSettings ShowQuotedLambdaComments
6363
/// Name anonymous types using the given <paramref name="nameFactory"/> instead of the
6464
/// default method.
6565
/// </summary>
66-
/// <param name="nameFactory">The factory method to execute to retrieve the name for an anonymous type.</param>
66+
/// <param name="nameFactory">
67+
/// The factory method to execute to retrieve the name for an anonymous type.
68+
/// </param>
6769
public TranslationSettings NameAnonymousTypesUsing(Func<Type, string> nameFactory)
6870
{
6971
AnonymousTypeNameFactory = nameFactory;
7072
return this;
7173
}
7274

7375
internal Func<Type, string> AnonymousTypeNameFactory { get; private set; }
76+
77+
/// <summary>
78+
/// Translate ConstantExpressions using the given <paramref name="valueFactory"/> instead of
79+
/// the default method.
80+
/// </summary>
81+
/// <param name="valueFactory">
82+
/// The factory method to execute to retrieve the ConstantExpression's translated value.
83+
/// </param>
84+
public TranslationSettings TranslateConstantsUsing(Func<Type, object, string> valueFactory)
85+
{
86+
ConstantExpressionValueFactory = valueFactory;
87+
return this;
88+
}
89+
90+
internal Func<Type, object, string> ConstantExpressionValueFactory { get; private set; }
7491
}
7592
}

ReadableExpressions/Translations/ConstantTranslation.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ internal static class ConstantTranslation
1919
{
2020
public static ITranslation For(ConstantExpression constant, ITranslationContext context)
2121
{
22+
if (context.Settings.ConstantExpressionValueFactory != null)
23+
{
24+
var userTranslation = context.Settings.ConstantExpressionValueFactory(constant.Type, constant.Value);
25+
26+
return FixedValueTranslation(userTranslation ?? "null", constant.Type);
27+
}
28+
2229
if (constant.Value == null)
2330
{
2431
return FixedValueTranslation("null", constant.Type);
@@ -561,7 +568,7 @@ public void WriteTo(TranslationBuffer buffer)
561568
buffer.WriteToTranslation(_timeSpan.Milliseconds);
562569
}
563570

564-
EndTranslation:
571+
EndTranslation:
565572
buffer.WriteToTranslation(')');
566573
}
567574

ReadableExpressions/Translations/MemberAccessTranslation.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace AgileObjects.ReadableExpressions.Translations
22
{
33
using System;
4+
using System.Reflection;
45
#if NET35
56
using Microsoft.Scripting.Ast;
67
#else
@@ -22,30 +23,36 @@ public MemberAccessTranslation(MemberExpression memberAccess, ITranslationContex
2223
}
2324

2425
private static ITranslation GetSubjectOrNull(MemberExpression memberAccess, ITranslationContext context)
26+
=> GetSubjectOrNull(memberAccess.Expression, memberAccess.Member, context);
27+
28+
protected static ITranslation GetSubjectOrNull(
29+
Expression subject,
30+
MemberInfo member,
31+
ITranslationContext context)
2532
{
26-
if (memberAccess.Expression == null)
33+
if (subject == null)
2734
{
28-
return context.GetTranslationFor(memberAccess.Member.DeclaringType);
35+
return context.GetTranslationFor(member.DeclaringType);
2936
}
3037

31-
if (SubjectIsCapturedInstance(memberAccess))
38+
if (SubjectIsCapturedInstance(subject, member))
3239
{
3340
return null;
3441
}
3542

36-
return context.GetTranslationFor(memberAccess.Expression);
43+
return context.GetTranslationFor(subject);
3744
}
3845

39-
private static bool SubjectIsCapturedInstance(MemberExpression memberAccess)
46+
private static bool SubjectIsCapturedInstance(Expression subject, MemberInfo member)
4047
{
41-
if (memberAccess.Expression.NodeType != ExpressionType.Constant)
48+
if (subject.NodeType != ExpressionType.Constant)
4249
{
4350
return false;
4451
}
4552

46-
var subjectType = ((ConstantExpression)memberAccess.Expression).Type;
53+
var subjectType = ((ConstantExpression)subject).Type;
4754

48-
return subjectType == memberAccess.Member.DeclaringType;
55+
return subjectType == member.DeclaringType;
4956
}
5057

5158
public MemberAccessTranslation(ITranslation subject, string memberName, Type memberType)
@@ -69,7 +76,7 @@ private int GetEstimatedSize()
6976
}
7077

7178
public ExpressionType NodeType => ExpressionType.MemberAccess;
72-
79+
7380
public Type Type { get; }
7481

7582
public int EstimatedSize { get; }

0 commit comments

Comments
 (0)