C# 在不指定所有输入参数类型的情况下获取方法引用

C# 在不指定所有输入参数类型的情况下获取方法引用,c#,generics,moq,C#,Generics,Moq,我想用Moq快速模拟一些方法调用。我确实有一些冗长的方法签名,具有很长的参数类型名称,尽管我很懒,但当方法设置为无论如何都失败时,不管输入参数的数量如何,我不想为所有签名键入It.IsAny()。 更具体地说,我提出了这个扩展方法,但是示例调用中没有绑定T1(第一个参数): public static void SetResult<T,T1,TResult>(this Mock<T> mock, Func<T, Func<T1, TResult>>

我想用Moq快速模拟一些方法调用。我确实有一些冗长的方法签名,具有很长的参数类型名称,尽管我很懒,但当方法设置为无论如何都失败时,不管输入参数的数量如何,我不想为所有签名键入
It.IsAny()

更具体地说,我提出了这个扩展方法,但是示例调用中没有绑定
T1
(第一个参数):

public static void SetResult<T,T1,TResult>(this Mock<T> mock, Func<T, Func<T1, TResult>> method, TResult result, Func<T1> filterT1 = null) 
   where T : class
{
    if(filterT1 == null) 
    { 
       filterT1 = It.IsAny<T1>; 
    }
    mock.Setup(m => method.Invoke(m).Invoke(filterT1.Invoke()))
        .Returns(result);
}

// I want to use this like the following
Mock<Foo> mock = /* ... */;
mock.SetResult(foo => foo.bar, "Some return value");                   // Doesn't work
mock.SetResult<Foo, int, string>(foo => foo.bar, "Some return value"); // Works
mock.SetResult(foo => foo.bar, "Some return value", () => 42);         // Works too
设置
需要一个
表达式
(注意
-输入类型缺少类型定义),并检查该表达式以模拟调用。任何
It.*
-只能在此表达式中进行调用(请参阅为什么使用
*.invoke()

endresult不仅是设置结果的方法,还包括异常、序列等

但我可以自己解决这个问题。

这可能需要一点改进,但它似乎与您所说的接近。唯一的区别是,它当前需要一个
MethodInfo
而不是
Func
来获得它应该模拟的方法

它迭代所有参数,并使用
System.Linq.Expression
为每个参数创建
IsAny
表达式

public static class MoqExtension
{
    public static void SetResult<T, TResult>(this Mock<T> mock, MethodInfo methodInfo, TResult result)
        where T : class
    {
        var expressions = new List<Expression>();
        // Create IsAny for each parameter
        foreach (var parameter in methodInfo.GetParameters())
        {
            var pType = parameter.ParameterType;

            var isAnyMethod = typeof(It).GetMethods((BindingFlags)(-1))
                .Where(x => x.Name == "IsAny")
                .First();

            var genericIsAnyMethod = isAnyMethod.MakeGenericMethod(pType);

            var isAnyExp = Expression.Call(genericIsAnyMethod);
            expressions.Add(isAnyExp);
        }

        // Create method call
        var argParam = Expression.Parameter(typeof(T), "x");
        var callExp = Expression.Call(argParam, methodInfo, expressions.ToArray());
        var lambda = Expression.Lambda<Func<T, TResult>>(callExp, argParam);

        // invoke setup method
        var mockType = mock.GetType();
        var setupMethod = mockType.GetMethods()
            .Where(x => x.Name == "Setup" && x.IsGenericMethod)
            .First();
        var genericMethod = setupMethod.MakeGenericMethod(typeof(TResult));

        var res = genericMethod.Invoke(mock, new object[] { lambda }) as ISetup<T, TResult>;

        res.Returns(result);
    }
}
公共静态类MoqExtension
{
公共静态void SetResult(此模拟模拟、MethodInfo MethodInfo、TResult结果)
T:在哪里上课
{
var表达式=新列表();
//为每个参数创建IsAny
foreach(methodInfo.GetParameters()中的var参数)
{
var pType=parameter.ParameterType;
var isAnyMethod=typeof(It).GetMethods((BindingFlags)(-1))
.Where(x=>x.Name==“IsAny”)
.First();
var genericanyMethod=isAnyMethod.MakeGenericMethod(pType);
var isAnyExp=Expression.Call(GenericsAnyMethod);
expressions.Add(isAnyExp);
}
//创建方法调用
var argParam=表达式参数(typeof(T),“x”);
var callExp=Expression.Call(argParam,methodInfo,expressions.ToArray());
var lambda=Expression.lambda(callExp,argParam);
//调用设置方法
var mockType=mock.GetType();
var setupMethod=mockType.GetMethods()
.Where(x=>x.Name==“Setup”&&x.IsGenericMethod)
.First();
var genericMethod=setupMethod.MakeGenericMethod(typeof(TResult));
var res=genericMethod.Invoke(mock,新对象[]{lambda})作为ISetup;
回复(结果);
}
}
用法示例:

    [Fact]
    public void MoqTestDynamic2()
    {
        var m = new Mock<ITestInterface>();

        m.SetResult(typeof(ITestInterface).GetMethod("GetAnotherInt"), 168);

        Assert.Equal(168, m.Object.GetAnotherInt("s", 1, 3));
        Assert.Equal(168, m.Object.GetAnotherInt("p", 1, 35));
        Assert.Equal(168, m.Object.GetAnotherInt(null, 1, 3));
    }

    public interface ITestInterface
    {
        int GetInt(string s);

        int GetAnotherInt(string s, int i, long l);
    }
[事实]
公共无效MoqTestDynamic2()
{
var m=新的Mock();
m、 SetResult(typeof(ITestInterface).GetMethod(“GetAnotherInt”),168;
Assert.Equal(168,m.Object.GetAnotherInt(“s”,1,3));
Assert.Equal(168,m.Object.GetAnotherInt(“p”,1,35));
Assert.Equal(168,m.Object.GetAnotherInt(null,1,3));
}
公共接口ITestInterface
{
int GetInt(字符串s);
int-GetAnotherInt(字符串s,int-i,长l);
}
一定有更好的方法来解决这个问题

  • 表示我们想要模拟的方法,而不是传递MethodInfo
  • 传入我们可能需要的任何非脱脂过滤器
  •     [Fact]
        public void MoqTestDynamic2()
        {
            var m = new Mock<ITestInterface>();
    
            m.SetResult(typeof(ITestInterface).GetMethod("GetAnotherInt"), 168);
    
            Assert.Equal(168, m.Object.GetAnotherInt("s", 1, 3));
            Assert.Equal(168, m.Object.GetAnotherInt("p", 1, 35));
            Assert.Equal(168, m.Object.GetAnotherInt(null, 1, 3));
        }
    
        public interface ITestInterface
        {
            int GetInt(string s);
    
            int GetAnotherInt(string s, int i, long l);
        }