У меня возникла очень любопытная проблема, в которой у меня не удается выполнить тесты с:
System.InvalidCastException: невозможно привести объект типа '<> c__DisplayClass18_0' к типу System.ComponentModel.INotifyPropertyChanged '.
Однако, когда я запускаю "тест отладки", тесты зеленые, если я запускаю отладку из модульных тестов или живых тестов "отладка".Поэтому сложно исследовать.Более того, у меня никогда не было проблем с использованием этих ICommand
реализаций в приложении.
У меня не должно быть никаких Exception
(у меня их нет в модульных тестах, у меня в живых тестах иVS не достигает (нарушает) исключение в этом случае, даже если это происходит).Как мне двигаться дальше?
Проблема приведения происходит в ListenForNotificationFrom((INotifyPropertyChanged) _executeDelegate.Target);
в классе DelegateCommandListen
.
РЕДАКТИРОВАТЬ: * * * * * * * Action<T>
является частной именованной функцией (1) или локальной функции (2) или лямбда-функции (2), ее свойство Target
четко определено и должно быть приведено к INotifyPropertyChanged
.Где (1) работает как для живых тестов, так и для модульных тестов, и (2) работает только в модульных тестах.
Моя ICommand
реализация:
public class DelegateCommandListen : ICommand
{
private readonly List<WeakReference> _controlEvent;
private Action<object> _executeDelegate;
public DelegateCommandListen(Action<object> executeDelegate, Predicate<object> canExecuteDelegate)
{
_controlEvent = new List<WeakReference>();
ExecuteDelegate = executeDelegate;
CanExecuteDelegate = canExecuteDelegate;
}
public Predicate<object> CanExecuteDelegate { get; set; }
public Action<object> ExecuteDelegate
{
get { return _executeDelegate; }
set
{
_executeDelegate = value;
ListenForNotificationFrom((INotifyPropertyChanged) _executeDelegate.Target);
}
}
public void RaiseCanExecuteChanged()
{
if (_controlEvent != null && _controlEvent.Count > 0)
_controlEvent.ForEach(ce => { ((EventHandler) ce.Target)?.Invoke(null, EventArgs.Empty); });
}
public DelegateCommandListen ListenOn<TObservedType, TPropertyType>
(TObservedType viewModel, Expression<Func<TObservedType, TPropertyType>> propertyExpression)
where TObservedType : INotifyPropertyChanged
{
var propertyName = GetPropertyName(propertyExpression);
viewModel.PropertyChanged += (s, e) =>
{
if (e.PropertyName == propertyName) RaiseCanExecuteChanged();
};
return this;
}
public void ListenForNotificationFrom<TObservedType>(TObservedType viewModel)
where TObservedType : INotifyPropertyChanged
{
viewModel.PropertyChanged += (s, e) => RaiseCanExecuteChanged();
}
private static string GetPropertyName<T, TProperty>(Expression<Func<T, TProperty>> expression)
where T : INotifyPropertyChanged
{
var lambda = expression as LambdaExpression;
var memberInfo = GetMemberExpression(lambda).Member;
return memberInfo.Name;
}
private static MemberExpression GetMemberExpression(LambdaExpression lambda)
{
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression body)
{
var unaryExpression = body;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
memberExpression = lambda.Body as MemberExpression;
return memberExpression;
}
public bool CanExecute(object parameter) => CanExecuteDelegate == null || CanExecuteDelegate(parameter);
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
_controlEvent.Add(new WeakReference(value));
}
remove
{
CommandManager.RequerySuggested -= value;
_controlEvent.Remove(_controlEvent.Find(r => (EventHandler) r.Target == value));
}
}
public void Execute(object parameter) => ExecuteDelegate?.Invoke(parameter);
}
Вот как я тестируюviewmodel:
[TestMethod]
public void NoTarget()
{
var sut = new DummyViewModel();
Assert.IsFalse(sut.IsSelected);
Assert.IsFalse(sut.ListenWithoutTargetCommand.CanExecute(null));
sut.IsSelected = true;
Assert.IsTrue(sut.ListenWithoutTargetCommand.CanExecute(null));
}
The ViewModel:
public class DummyViewModel : INotifyPropertyChanged
{
private ICommand _listenWith1TargetCommand;
private bool _isSelected;
public string Result { get; set; }
public bool IsSelected
{
get => _isSelected;
set
{
if (value == _isSelected) return;
_isSelected = value;
OnPropertyChanged();
}
}
public ICommand ListenWith1TargetCommand
{
get
{
return _listenWith1TargetCommand ?? (_listenWith1TargetCommand = new DelegateCommandListen(
s => { Result = "Executing listen command 1"; }, // lambda|local function|named function
s => IsSelected)
.ListenOn(this, o => o.IsSelected));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Полный источник: https://github.com/mprevot/ReproLiveTests