c# - ICommand 实现中不可复制的 System.InvalidCastException(Roslyn 错误?)
问题描述
我有一个非常奇怪的问题,我的测试失败了:
System.InvalidCastException:无法将“<>c__DisplayClass18_0”类型的对象转换为“System.ComponentModel.INotifyPropertyChanged”类型。
但是,当我运行“调试测试”时,测试是绿色的,无论我是从单元测试还是实时测试“调试”运行调试。因此,调查起来很棘手。ICommand
此外,我在应用程序中使用这些实现从未遇到任何问题。
我不应该有任何Exception
(我没有任何单元测试,我有实时测试,并且在这种情况下 VS 没有达到(破坏)异常,即使它正在发生)。我该如何前进?
演员表问题发生在ListenForNotificationFrom((INotifyPropertyChanged) _executeDelegate.Target);
课堂DelegateCommandListen
上。
编辑:无论Action<T>
是私有命名函数 (1) 还是局部函数 (2) 或 lambda (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);
}
这是我测试视图模型的方法:
[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));
}
视图模型:
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));
}
}
解决方案
根据 Artur Spychaj (MSFT) 的说法,测试失败是因为 LUT(实时单元测试)修改了 lambda 以捕获覆盖信息,并且编译器将 lambda 移动到一个单独的类中。this
解决方法是作为单独的参数传递DelegateCommandListen
.
推荐阅读
- javascript - 无法编译和工作 getColumnSearchProps 和表重置过滤器和排序器不起作用
- google-smart-home - 话语“房间里的温度是多少”总是报告设备已关闭
- php - mysqli_fetch_assoc() 期望参数 1 为 mysqli_result, bool given , logic earro
- python - 熊猫到 Json 格式问题
- javascript - react-native-camera 方向改变冻结
- python - 字典中存在键时引发 KeyError
- reactjs - GraphQL 查询不返回带有 id 参数的结果
- javascript - React - 我将状态从子组件传递给父组件,但状态值在一个更新周期内关闭
- javascript - nodejs将JSON写入特定路径
- sql - 如何在 Oracle 数据库中插入日期和时间?