Skip to content

Commit 1830608

Browse files
authored
Merge pull request #758 from gorsheninmv/resourcetype-with-implicit-cast
Resourcetype with implicit and explicit cast
2 parents f5e8b16 + 5b693d5 commit 1830608

File tree

5 files changed

+178
-8
lines changed

5 files changed

+178
-8
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ artifacts/*
3737
*.DotSettings.user
3838
# Visual Studio 2015 cache/options directory
3939
.vs/
40+
.idea/
4041

4142
[R|r]elease/**

src/CommandLine/CastExtensions.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection;
4+
5+
namespace CommandLine
6+
{
7+
public static class CastExtensions
8+
{
9+
private const string ImplicitCastMethodName = "op_Implicit";
10+
private const string ExplicitCastMethodName = "op_Explicit";
11+
12+
public static bool CanCast<T>(this Type baseType)
13+
{
14+
return baseType.CanImplicitCast<T>() || baseType.CanExplicitCast<T>();
15+
}
16+
17+
public static bool CanCast<T>(this object obj)
18+
{
19+
var objType = obj.GetType();
20+
return objType.CanCast<T>();
21+
}
22+
23+
public static T Cast<T>(this object obj)
24+
{
25+
try
26+
{
27+
return (T) obj;
28+
}
29+
catch (InvalidCastException)
30+
{
31+
if (obj.CanImplicitCast<T>())
32+
return obj.ImplicitCast<T>();
33+
if (obj.CanExplicitCast<T>())
34+
return obj.ExplicitCast<T>();
35+
else
36+
throw;
37+
}
38+
}
39+
40+
private static bool CanImplicitCast<T>(this Type baseType)
41+
{
42+
return baseType.CanCast<T>(ImplicitCastMethodName);
43+
}
44+
45+
private static bool CanImplicitCast<T>(this object obj)
46+
{
47+
var baseType = obj.GetType();
48+
return baseType.CanImplicitCast<T>();
49+
}
50+
51+
private static bool CanExplicitCast<T>(this Type baseType)
52+
{
53+
return baseType.CanCast<T>(ExplicitCastMethodName);
54+
}
55+
56+
private static bool CanExplicitCast<T>(this object obj)
57+
{
58+
var baseType = obj.GetType();
59+
return baseType.CanExplicitCast<T>();
60+
}
61+
62+
private static bool CanCast<T>(this Type baseType, string castMethodName)
63+
{
64+
var targetType = typeof(T);
65+
return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)
66+
.Where(mi => mi.Name == castMethodName && mi.ReturnType == targetType)
67+
.Any(mi =>
68+
{
69+
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
70+
return pi != null && pi.ParameterType == baseType;
71+
});
72+
}
73+
74+
private static T ImplicitCast<T>(this object obj)
75+
{
76+
return obj.Cast<T>(ImplicitCastMethodName);
77+
}
78+
79+
private static T ExplicitCast<T>(this object obj)
80+
{
81+
return obj.Cast<T>(ExplicitCastMethodName);
82+
}
83+
84+
private static T Cast<T>(this object obj, string castMethodName)
85+
{
86+
var objType = obj.GetType();
87+
MethodInfo conversionMethod = objType.GetMethods(BindingFlags.Public | BindingFlags.Static)
88+
.Where(mi => mi.Name == castMethodName && mi.ReturnType == typeof(T))
89+
.SingleOrDefault(mi =>
90+
{
91+
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
92+
return pi != null && pi.ParameterType == objType;
93+
});
94+
if (conversionMethod != null)
95+
return (T) conversionMethod.Invoke(null, new[] {obj});
96+
else
97+
throw new InvalidCastException($"No method to cast {objType.FullName} to {typeof(T).FullName}");
98+
}
99+
}
100+
}

src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
42
using System.Reflection;
5-
using System.Text;
63

74
namespace CommandLine.Infrastructure
85
{
@@ -43,15 +40,16 @@ private string GetLocalizedValue()
4340
return _value;
4441
if (_localizationPropertyInfo == null)
4542
{
46-
// Static class IsAbstract
43+
// Static class IsAbstract
4744
if (!_type.IsVisible)
4845
throw new ArgumentException($"Invalid resource type '{_type.FullName}'! {_type.Name} is not visible for the parser! Change resources 'Access Modifier' to 'Public'", _propertyName);
49-
PropertyInfo propertyInfo = _type.GetProperty(_value, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static);
50-
if (propertyInfo == null || !propertyInfo.CanRead || propertyInfo.PropertyType != typeof(string))
46+
PropertyInfo propertyInfo = _type.GetProperty(_value, BindingFlags.Public | BindingFlags.Static);
47+
if (propertyInfo == null || !propertyInfo.CanRead || (propertyInfo.PropertyType != typeof(string) && !propertyInfo.PropertyType.CanCast<string>()))
5148
throw new ArgumentException("Invalid resource property name! Localized value: {_value}", _propertyName);
5249
_localizationPropertyInfo = propertyInfo;
5350
}
54-
return (string)_localizationPropertyInfo.GetValue(null, null);
51+
52+
return _localizationPropertyInfo.GetValue(null, null).Cast<string>();
5553
}
5654
}
5755

tests/CommandLine.Tests/Fakes/ResourceFakes.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@
33
public static class StaticResource
44
{
55
public static string HelpText { get { return "Localized HelpText"; } }
6+
public static TypeWithImplicitCast ImplicitCastHelpText => new TypeWithImplicitCast("Localized HelpText");
7+
public static TypeWithExplicitCast ExplicitCastHelpText => new TypeWithExplicitCast("Localized HelpText");
8+
public static TypeWithWrongImplicitCast WrongImplicitCastHelpText => new TypeWithWrongImplicitCast();
9+
public static TypeWithWrongExplicitCast WrongExplicitCastHelpText => new TypeWithWrongExplicitCast();
610
}
711

812
public class NonStaticResource
913
{
1014
public static string HelpText { get { return "Localized HelpText"; } }
1115
public static string WriteOnlyText { set { value?.ToString(); } }
1216
private static string PrivateHelpText { get { return "Localized HelpText"; } }
17+
public static TypeWithImplicitCast ImplicitCastHelpText => new TypeWithImplicitCast("Localized HelpText");
18+
public static TypeWithExplicitCast ExplicitCastHelpText => new TypeWithExplicitCast("Localized HelpText");
19+
public static TypeWithWrongImplicitCast WrongImplicitCastHelpText => new TypeWithWrongImplicitCast();
20+
public static TypeWithWrongExplicitCast WrongExplicitCastHelpText => new TypeWithWrongExplicitCast();
1321
}
1422

1523
public class NonStaticResource_WithNonStaticProperty
@@ -22,4 +30,59 @@ internal class InternalResource
2230
public static string HelpText { get { return "Localized HelpText"; } }
2331
}
2432

33+
public class TypeWithImplicitCast
34+
{
35+
private string value;
36+
37+
public TypeWithImplicitCast(string value)
38+
{
39+
this.value = value;
40+
}
41+
42+
public static implicit operator string(TypeWithImplicitCast obj)
43+
{
44+
return obj.value;
45+
}
46+
47+
public static implicit operator int(TypeWithImplicitCast obj)
48+
{
49+
return 0;
50+
}
51+
}
52+
53+
public class TypeWithWrongImplicitCast
54+
{
55+
public static implicit operator int(TypeWithWrongImplicitCast obj)
56+
{
57+
return 0;
58+
}
59+
}
60+
61+
public class TypeWithExplicitCast
62+
{
63+
private string value;
64+
65+
public TypeWithExplicitCast(string value)
66+
{
67+
this.value = value;
68+
}
69+
70+
public static explicit operator string(TypeWithExplicitCast obj)
71+
{
72+
return obj.value;
73+
}
74+
75+
public static explicit operator int(TypeWithExplicitCast obj)
76+
{
77+
return 0;
78+
}
79+
}
80+
81+
public class TypeWithWrongExplicitCast
82+
{
83+
public static explicit operator int(TypeWithWrongExplicitCast obj)
84+
{
85+
return 0;
86+
}
87+
}
2588
}

tests/CommandLine.Tests/Unit/BaseAttributeTests.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ public static void Default(object defaultValue)
2121
[InlineData("Help text", null, "Help text")]
2222
[InlineData("HelpText", typeof(Fakes.StaticResource), "Localized HelpText")]
2323
[InlineData("HelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")]
24+
[InlineData("ImplicitCastHelpText", typeof(Fakes.StaticResource), "Localized HelpText")]
25+
[InlineData("ImplicitCastHelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")]
26+
[InlineData("ExplicitCastHelpText", typeof(Fakes.StaticResource), "Localized HelpText")]
27+
[InlineData("ExplicitCastHelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")]
2428
public static void HelpText(string helpText, Type resourceType, string expected)
2529
{
2630
TestBaseAttribute baseAttribute = new TestBaseAttribute();
2731
baseAttribute.HelpText = helpText;
2832
baseAttribute.ResourceType = resourceType;
29-
33+
3034
Assert.Equal(expected, baseAttribute.HelpText);
3135
}
3236

@@ -35,6 +39,10 @@ public static void HelpText(string helpText, Type resourceType, string expected)
3539
[InlineData("WriteOnlyText", typeof(Fakes.NonStaticResource))]
3640
[InlineData("PrivateOnlyText", typeof(Fakes.NonStaticResource))]
3741
[InlineData("HelpText", typeof(Fakes.InternalResource))]
42+
[InlineData("WrongImplicitCastHelpText", typeof(Fakes.StaticResource))]
43+
[InlineData("WrongExplicitCastHelpText", typeof(Fakes.StaticResource))]
44+
[InlineData("WrongImplicitCastHelpText", typeof(Fakes.NonStaticResource))]
45+
[InlineData("WrongExplicitCastHelpText", typeof(Fakes.NonStaticResource))]
3846
public void ThrowsHelpText(string helpText, Type resourceType)
3947
{
4048
TestBaseAttribute baseAttribute = new TestBaseAttribute();

0 commit comments

Comments
 (0)