如何使用私有类型调用方法<t>(Func <action <t>&gt;操作)

时间:2017-05-31 23:20:57

标签: c# generics reflection

我希望代码剪切说明我的问题。 我需要Invoke CallEvent方法,就像它在out-comment行中一样。 我无法访问ThirdPartyAnotherThirdParty类 就我而言:

public class ThirdParty
{
    private struct MsgType
    { }

    private static void AnotherFunc(MsgType msg)
    { }
}

public class AnotherThirdParty
{
    public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
    { }
}

public class MyClass
{
    public static void Main()
    {
        Type MsgType = typeof(ThirdParty).GetNestedType(
            "MsgType", BindingFlags.Instance | BindingFlags.NonPublic);
        object msg = Activator.CreateInstance(MsgType);

        MethodInfo CallEvent = typeof(AnotherThirdParty).GetMethod("CallEvent");
        CallEvent = CallEvent.MakeGenericMethod(MsgType);

        MethodInfo AnotherFunc = typeof(ThirdParty).GetMethod(
            "AnotherFunc", BindingFlags.Static | BindingFlags.NonPublic);

        CallEvent.Invoke(null, new object[] {???, msg});
        //CallEvent<MsgType>((int x) => new Action<MsgType>(AnotherFunc), msg);
        // I can't get my head around how to solve this (Action<msgtype>)
    }
}

我也尝试过:

CallEvent.Invoke(null, new object[]
    {
        new Func<int, Action<object>>((int x) =>
            new Action<object>((object y) =>
                AnotherFunc.Invoke(null, new object[] { y }))),
        msg
    });

我得到以下例外:

  

System.ArgumentException:类型的对象   'System.Func2 [System.Int32,System.Action1 [System.Object]]'不能   转换为类型   “System.Func2 [System.Int32,System.Action1 [第三方+ MSGTYPE]]。

我该怎么办?

4 个答案:

答案 0 :(得分:7)

public class ThirdParty
{
    private struct MsgType { }
    private static void AnotherFunc(MsgType msg)
    {
        // Inserted to demonstrate getting here
        Console.WriteLine($"HEY: {msg}");
    }
}

public class AnotherThirdParty
{
    public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
    {
        // Inserted to demonstrate calling the func and then
        // the action
        action(12)(arg);
    }
}

public static void Main()
{
    var msgTypeType = 
        typeof(ThirdParty).GetNestedType("MsgType", BindingFlags.NonPublic);

    // This is the message type we're passing (presumably you'll do more with it)
    var ourMsgTypeArg = Activator.CreateInstance(msgTypeType);

    // Get the reference to the CallEvent method
    var callEventMethod =
        typeof(AnotherThirdParty).GetMethod("CallEvent", BindingFlags.Public | BindingFlags.Static)
        .MakeGenericMethod(msgTypeType);

    // Get the reference to the AnotherFunc method
    var anotherFunc =
        typeof(ThirdParty).GetMethod("AnotherFunc", BindingFlags.NonPublic | BindingFlags.Static);

    // Build the func to pass along to CallEvent
    var func = CreateFunc(msgTypeType, anotherFunc);

    // Call the CallEvent<MsgType> method.
    callEventMethod.Invoke(null, new object[] {
        func,
        ourMsgTypeArg
    });
}

private static Delegate CreateFunc(Type msgType, MethodInfo anotherFunc)
{
    // The func takes an int
    var intArg = Expression.Parameter(typeof(int));

    // The action takes a msgType
    var msgTypeArg = Expression.Parameter(msgType);

    // Represent the call out to "AnotherFunc"
    var call = Expression.Call(null, anotherFunc, msgTypeArg);

    // Build the action to just make the call to "AnotherFunc"
    var action = Expression.Lambda(call, msgTypeArg);

    // Build the func to just return the action
    var func = Expression.Lambda(action, intArg);

    // Compile the chain and send it out
    return func.Compile();
}

此代码按您的要求运行并打印以下内容:

HEY: UserQuery+ThirdParty+MsgType

答案 1 :(得分:2)

这似乎在运行:

    MethodInfo miCreateDelegate = typeof(MethodInfo).GetMethod("CreateDelegate", new[] { typeof(Type), typeof(Object) });
    var ActionType = typeof(Action<>).MakeGenericType(MsgType);
    var lambdabody = Expression.Convert(Expression.Call(Expression.Constant(AnotherFunc), miCreateDelegate, new[] { Expression.Constant(ActionType), Expression.Constant(null) }), ActionType);
    var intparm = Expression.Parameter(typeof(int));
    var lambda = Expression.Lambda(lambdabody, intparm);

    CallEvent.Invoke(null, new object[] {
        lambda.Compile(),
        msg
    });

更完整的答案是我是如何产生的?我使用LINQPad来编译一个更简单,类似的表达式,将string替换为MsgTypeExpression

public static void afunc(string x) { }

Expression<Func<int, Action<string>>> lambda = (int x) => new Action<string>(afunc);

然后我使用LINQPad Dump()函数输出表达式树。

lambda.Dump();

然后,MSDN Expression文档中的一些洞察力给了我正确的静态方法来创建片段。我已经知道如何从LINQPad的扩展方法实例化泛型类型,它可以动态创建匿名类型以扩展Dump()以从匿名对象中排除字段,我知道如何从扩展LINQ的扩展方法创建lambdas一个适当的SQL可翻译的左和右连接操作。

答案 2 :(得分:1)

使用Delegate.CreateDelegate方法构造Action<MsgType>对象。使用Expression.Lambda<>构建Func<int,Action<T>>

var actionType = typeof(Action<>).MakeGenericType(MsgType);
var funcType = typeof(Func<,>).MakeGenericType(typeof(int), actionType);
var p1 = Expression.Parameter(typeof(int));
var p2 = Expression.Parameter(actionType);
var delegate = Expression.Constant(Delegate.CreateDelegate(actionType, AnotherFunc), funcType);
var lambda = Expression.Lambda(delegate, p1, p2);
CallEvent.Invoke(null, new object[] {
    lambda.Compile()
,   msg
});

答案 3 :(得分:0)

这将起作用并将打印A但功能工厂对我来说是个谜,所以我刚刚返回创建的委托。这与.net标准1.1

兼容
static void Main(string[] args)
        {
             Type MsgType = typeof(ThirdParty).GetNestedType(
                "MsgType", BindingFlags.Instance | BindingFlags.NonPublic);
        object msg = Activator.CreateInstance(MsgType);

        MethodInfo CallEvent = typeof(AnotherThirdParty).GetMethod("CallEvent");
        CallEvent = CallEvent.MakeGenericMethod(MsgType);

        MethodInfo AnotherFunc = typeof(ThirdParty).GetMethod(
            "AnotherFunc", BindingFlags.Static | BindingFlags.NonPublic);

        var actionType = typeof(Action<>).MakeGenericType(MsgType);

        var actionDelegate = AnotherFunc.CreateDelegate(actionType);

        var param = Expression.Parameter(typeof(int));
        var funcDelegate = Expression.Lambda(Expression.Constant(actionDelegate),param).Compile();

        CallEvent.Invoke(null, new []{ funcDelegate, msg });


        Console.ReadLine();

        }

        public class ThirdParty
        {
            private struct MsgType
            { }

            private static void AnotherFunc(MsgType msg)
            {
                Console.WriteLine("A");
            }
        }

        public class AnotherThirdParty
        {
            public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
            {
                action(1)(arg);
            }
        }