Browse Source

Add error handling for methods with too many parameters. Add support for static methods.

pull/1179/head
Jeremy Koritzinsky 9 years ago
parent
commit
81168ac094
  1. 5
      src/Markup/Avalonia.Markup/AlwaysEnabledDelegateCommand.cs
  2. 41
      src/Markup/Avalonia.Markup/Data/Plugins/MethodAccessorPlugin.cs
  3. 22
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Method.cs

5
src/Markup/Avalonia.Markup/AlwaysEnabledDelegateCommand.cs

@ -16,10 +16,7 @@ namespace Avalonia.Markup
public event EventHandler CanExecuteChanged; public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) public bool CanExecute(object parameter) => true;
{
return true;
}
public void Execute(object parameter) public void Execute(object parameter)
{ {

41
src/Markup/Avalonia.Markup/Data/Plugins/MethodAccessorPlugin.cs

@ -9,24 +9,30 @@ namespace Avalonia.Markup.Data.Plugins
{ {
class MethodAccessorPlugin : IPropertyAccessorPlugin class MethodAccessorPlugin : IPropertyAccessorPlugin
{ {
public bool Match(object obj, string propertyName) public bool Match(object obj, string methodName)
=> obj.GetType().GetRuntimeMethods().FirstOrDefault(x => x.Name == propertyName) != null; => obj.GetType().GetRuntimeMethods().Any(x => x.Name == methodName);
public IPropertyAccessor Start(WeakReference reference, string propertyName) public IPropertyAccessor Start(WeakReference reference, string methodName)
{ {
Contract.Requires<ArgumentNullException>(reference != null); Contract.Requires<ArgumentNullException>(reference != null);
Contract.Requires<ArgumentNullException>(propertyName != null); Contract.Requires<ArgumentNullException>(methodName != null);
var instance = reference.Target; var instance = reference.Target;
var method = instance.GetType().GetRuntimeMethods().FirstOrDefault(x => x.Name == propertyName); var method = instance.GetType().GetRuntimeMethods().FirstOrDefault(x => x.Name == methodName);
if (method != null) if (method != null)
{ {
if (method.GetParameters().Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8)
{
var exception = new ArgumentException("Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.", nameof(method));
return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
}
return new Accessor(reference, method); return new Accessor(reference, method);
} }
else else
{ {
var message = $"Could not find CLR method '{propertyName}' on '{instance}'"; var message = $"Could not find CLR method '{methodName}' on '{instance}'";
var exception = new MissingMemberException(message); var exception = new MissingMemberException(message);
return new PropertyError(new BindingNotification(exception, BindingErrorType.Error)); return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
} }
@ -41,9 +47,7 @@ namespace Avalonia.Markup.Data.Plugins
var paramTypes = method.GetParameters().Select(param => param.ParameterType).ToArray(); var paramTypes = method.GetParameters().Select(param => param.ParameterType).ToArray();
var returnType = method.ReturnType; var returnType = method.ReturnType;
// TODO: Throw exception if more than 8 parameters or more than 7 + return type.
// Do this here or in the caller? Here probably
if (returnType == typeof(void)) if (returnType == typeof(void))
{ {
if (paramTypes.Length == 0) if (paramTypes.Length == 0)
@ -60,10 +64,8 @@ namespace Avalonia.Markup.Data.Plugins
var genericTypeParameters = paramTypes.Concat(new[] { returnType }).ToArray(); var genericTypeParameters = paramTypes.Concat(new[] { returnType }).ToArray();
PropertyType = Type.GetType($"System.Func`{genericTypeParameters.Length}").MakeGenericType(genericTypeParameters); PropertyType = Type.GetType($"System.Func`{genericTypeParameters.Length}").MakeGenericType(genericTypeParameters);
} }
// TODO: Is this going to leak? Value = method.IsStatic ? method.CreateDelegate(PropertyType) : method.CreateDelegate(PropertyType, reference.Target);
// TODO: Static methods?
Value = method.CreateDelegate(PropertyType, reference.Target);
} }
public override Type PropertyType { get; } public override Type PropertyType { get; }
@ -73,19 +75,18 @@ namespace Avalonia.Markup.Data.Plugins
public override bool SetValue(object value, BindingPriority priority) => false; public override bool SetValue(object value, BindingPriority priority) => false;
protected override void SubscribeCore(IObserver<object> observer) protected override void SubscribeCore(IObserver<object> observer)
{
SendCurrentValue();
}
private void SendCurrentValue()
{ {
try try
{ {
var value = Value; Observer.OnNext(Value);
Observer.OnNext(value);
} }
catch { } catch { }
} }
private void SendCurrentValue()
{
}
} }
} }
} }

22
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Method.cs

@ -19,6 +19,11 @@ namespace Avalonia.Markup.UnitTests.Data
public int MethodWithReturn() => 0; public int MethodWithReturn() => 0;
public int MethodWithReturnAndParameters(int i) => i; public int MethodWithReturnAndParameters(int i) => i;
public static void StaticMethod() { }
public static void TooManyParameters(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { }
public static int TooManyParametersWithReturnType(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) => 1;
} }
[Fact] [Fact]
@ -37,6 +42,7 @@ namespace Avalonia.Markup.UnitTests.Data
[InlineData(nameof(TestObject.MethodWithoutReturn), typeof(Action))] [InlineData(nameof(TestObject.MethodWithoutReturn), typeof(Action))]
[InlineData(nameof(TestObject.MethodWithReturn), typeof(Func<int>))] [InlineData(nameof(TestObject.MethodWithReturn), typeof(Func<int>))]
[InlineData(nameof(TestObject.MethodWithReturnAndParameters), typeof(Func<int, int>))] [InlineData(nameof(TestObject.MethodWithReturnAndParameters), typeof(Func<int, int>))]
[InlineData(nameof(TestObject.StaticMethod), typeof(Action))]
public async Task Should_Get_Method_WithCorrectDelegateType(string methodName, Type expectedType) public async Task Should_Get_Method_WithCorrectDelegateType(string methodName, Type expectedType)
{ {
var data = new TestObject(); var data = new TestObject();
@ -61,5 +67,21 @@ namespace Avalonia.Markup.UnitTests.Data
GC.KeepAlive(data); GC.KeepAlive(data);
} }
[Theory]
[InlineData(nameof(TestObject.TooManyParameters))]
[InlineData(nameof(TestObject.TooManyParametersWithReturnType))]
public async Task Should_Return_Error_Notification_If_Too_Many_Parameters(string methodName)
{
var data = new TestObject();
var observer = new ExpressionObserver(data, methodName);
var result = await observer.Take(1);
Assert.IsType<BindingNotification>(result);
Assert.Equal(BindingErrorType.Error, ((BindingNotification)result).ErrorType);
GC.KeepAlive(data);
}
} }
} }

Loading…
Cancel
Save