为什么我不能为 Moq 中的 List Find 方法创建回调?
Why can't I create a callback for the List Find method in Moq?
我创建了一个扩展方法,允许我将 List 视为 DbSet 以用于测试目的(实际上,我在此处关于堆栈溢出的另一个问题中发现了这个想法,它非常有用)。编码如下:
public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var mockDbSet = new Mock<DbSet<T>>();
mockDbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockDbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockDbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockDbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
mockDbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
mockDbSet.Setup(d => d.Find(It.IsAny<object[]>())).Callback(sourceList.Find);
return mockDbSet.Object;
}
我使用 Add 有一段时间了,效果很好。但是,当我尝试为 Find 添加回调时,出现编译器错误,指出它无法将方法组转换为操作。为什么sourceList.Add是一个Action,而sourceList.Find是一个方法组?
我承认我对 C# 委托不是特别熟悉,所以我很可能遗漏了一些非常明显的东西。提前致谢。
Add
起作用的原因是因为 List<T>.Add
方法组包含一个方法,该方法采用 T
类型的单个参数并且 return 为 void。此方法与 Action<T>
具有相同的签名,Action<T>
是 Callback
方法的重载之一(具有单个泛型类型参数 Callback<T>
的方法),因此 List<T>.Add
方法组可以转换为 Action<T>
.
使用 Find
,您正在尝试调用 Callback
方法(与 Callback<T>
相反),该方法需要 Action
参数(与 Action<T>
).这里的区别在于 Action
不接受任何参数,而 Action<T>
接受类型 T 的单个参数。List<T>.Find
方法组不能转换为 Action
因为所有 Find
方法(反正只有一个)都接受输入参数。
以下将编译:
public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
{
var mockDbSet = new Mock<DbSet<T>>();
mockDbSet.Setup(d => d.Find(It.IsAny<object[]>())).Callback<Predicate<T>>(t => sourceList.Find(t));
return mockDbSet.Object;
}
请注意,我调用了 .Callback<Predicate<T>>
,因为 List<T>.Find
方法需要 Predicate 类型的参数。另请注意,我不得不写 t => sourceList.Find(t)
而不是 sourceList.Find
,因为 Find
return 是一个值(这意味着它与 Action<Predicate<T>>
的签名不匹配)。通过将其写成 lambda 表达式,return 值将被丢弃。
请注意,虽然此编译它实际上不会工作,因为 DbSet.Find
方法实际上采用 object[]
作为参数,而不是 Predicate<T>
,因此您可能必须这样做像这样:
public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
{
var mockDbSet = new Mock<DbSet<T>>();
mockDbSet.Setup(d => d.Find(It.IsAny<object[]>())).Callback<object[]>(keyValues => sourceList.Find(keyValues.Contains));
return mockDbSet.Object;
}
这最后一点与如何使用 Moq 库、如何使用方法组、委托和 lambda 有更多关系——这一行有各种各样的语法糖,隐藏了与以下内容实际相关的内容编译器,什么不是。
我创建了一个扩展方法,允许我将 List 视为 DbSet 以用于测试目的(实际上,我在此处关于堆栈溢出的另一个问题中发现了这个想法,它非常有用)。编码如下:
public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var mockDbSet = new Mock<DbSet<T>>();
mockDbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockDbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockDbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockDbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
mockDbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
mockDbSet.Setup(d => d.Find(It.IsAny<object[]>())).Callback(sourceList.Find);
return mockDbSet.Object;
}
我使用 Add 有一段时间了,效果很好。但是,当我尝试为 Find 添加回调时,出现编译器错误,指出它无法将方法组转换为操作。为什么sourceList.Add是一个Action,而sourceList.Find是一个方法组?
我承认我对 C# 委托不是特别熟悉,所以我很可能遗漏了一些非常明显的东西。提前致谢。
Add
起作用的原因是因为 List<T>.Add
方法组包含一个方法,该方法采用 T
类型的单个参数并且 return 为 void。此方法与 Action<T>
具有相同的签名,Action<T>
是 Callback
方法的重载之一(具有单个泛型类型参数 Callback<T>
的方法),因此 List<T>.Add
方法组可以转换为 Action<T>
.
使用 Find
,您正在尝试调用 Callback
方法(与 Callback<T>
相反),该方法需要 Action
参数(与 Action<T>
).这里的区别在于 Action
不接受任何参数,而 Action<T>
接受类型 T 的单个参数。List<T>.Find
方法组不能转换为 Action
因为所有 Find
方法(反正只有一个)都接受输入参数。
以下将编译:
public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
{
var mockDbSet = new Mock<DbSet<T>>();
mockDbSet.Setup(d => d.Find(It.IsAny<object[]>())).Callback<Predicate<T>>(t => sourceList.Find(t));
return mockDbSet.Object;
}
请注意,我调用了 .Callback<Predicate<T>>
,因为 List<T>.Find
方法需要 Predicate 类型的参数。另请注意,我不得不写 t => sourceList.Find(t)
而不是 sourceList.Find
,因为 Find
return 是一个值(这意味着它与 Action<Predicate<T>>
的签名不匹配)。通过将其写成 lambda 表达式,return 值将被丢弃。
请注意,虽然此编译它实际上不会工作,因为 DbSet.Find
方法实际上采用 object[]
作为参数,而不是 Predicate<T>
,因此您可能必须这样做像这样:
public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
{
var mockDbSet = new Mock<DbSet<T>>();
mockDbSet.Setup(d => d.Find(It.IsAny<object[]>())).Callback<object[]>(keyValues => sourceList.Find(keyValues.Contains));
return mockDbSet.Object;
}
这最后一点与如何使用 Moq 库、如何使用方法组、委托和 lambda 有更多关系——这一行有各种各样的语法糖,隐藏了与以下内容实际相关的内容编译器,什么不是。