2017-05-31 46 views
9

Invoke方法 (Func鍵 >動作),我希望剪斷的代碼說明我的問題。 我需要 Invoke CallEvent方法就像它是在沒有評論的行。 我沒有訪問 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: 類型的對象「系統.Func2 [System.Int32,System.Action1 [System.Object的]] '不能是 轉換爲類型 ' System.Func2 [System.Int32,Sys系統tem.Action1 [第三方+ MSGTYPE]。

我應該怎麼做?

+0

是否'AnotherThirdParty.CallEvent'做了'T' ARG(比它發送過動作除外)什麼? – Clint

+0

是的,它調用另一個方法'的RaiseEvent (T1 ARG1,T3 ARG3,Func鍵>行動,T2 ARG2)其中,T1:INT其中T3:int' – timo

+0

應該採取什麼廠的功能呢?只需返回每個int或其他東西的方法? –

回答

5
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 
+0

同樣的問題,我與該方法沒有做任何事情。但是他這樣做可能是錯誤的做法。 –

+0

@FilipCordas你能否詳細說明,因爲這確實有效。由OP發佈的方法不會做任何事情,因爲我想他們只是想擁有一個足夠簡單的代碼塊。 – Clint

+1

我並不清楚我的意思是答案是咧嘴。錯誤的部分是不得不在第一時間做到這一點。 –

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 
}); 
+0

System.ArgumentException:類型'System.Action'1 [ThirdParty + MsgTyype]'的對象無法轉換鍵入'System.Func'2 [System.Int32,System.Action'1 [ThirdParty + MsgTyype]]'。 – timo

+0

失蹤的Func >部分..注意這不會工作.net標準1.1 –

+0

@timohehnen現在應該修復。 – dasblinkenlight

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); 
      } 
     } 
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, new[] { intparm }); 

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

更完整的答案是我是如何產生這個的?我用LINQPad編譯一個更簡單的,相似的表達代stringMsgTypeExpression

public static void afunc(string x) { } 

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

然後我用的LINQPad Dump()函數來輸出表達式樹。

lambda.Dump(); 

然後MSDN Expression文檔中的一些spelunking給了我正確的靜態方法來創建件。我已經知道如何從LINQPad的擴展方法實例化泛型類型,它可以動態地創建匿名類型來擴展Dump()以從匿名對象中排除字段,並且我知道如何從擴展方法創建lambda表達式,該擴展方法使用適當的SQL-可轉換的左和右連接操作。

+0

這很好,但它涉及到投擲到CreateDelegate周圍,它可以在沒有它的情況下完成。 (也很高興看到別人在玩Expression:P) – Clint

+0

我添加了更多解釋,但這是編譯器如何將lambda調用轉換爲'new Action <>'。 – NetMage