![](/img/trans.png)
[英]Use Reflection to get methods excluding local functions in C# 7.0?
[英]Reflection: How do I find and invoke a local functon in C# 7.0?
我有一個私有靜態泛型方法,我想使用反射調用,但實際上我想將它“捆綁”在另一個方法中。 C# 7.0 支持本地函數,所以這絕對是可能的。
你會說“為什么不直接調用它?” 但我使用它來獲得以強類型方式使用對象和 System.Type 的能力,因此我需要動態調用它。 如果我擁有它,此代碼已經可以工作,因為它是自己的私有靜態泛型方法。
private static void HandleResponse(object data, Type asType)
{
var application = typeof(Program);
application
.GetMethod(nameof(UseAs), BindingFlags.Static | BindingFlags.NonPublic)
.MakeGenericMethod(asType)
.Invoke(null, new object[] { data });
}
public static void UseAs<T>(T obj)
{
Console.WriteLine($"Object is now a: {typeof(T)}:");
};
上面的代碼有效。 如果我傳入:
data: new TestObject(),
type: typeof(TestObject)
我實際上在 UseAs 中有一個 TestObject。
所以,我想把這一切放在一個方法中,就像這樣:
private static void HandleResponse(object data, Type asType)
{
void useAs<T>(T obj)
{
Console.WriteLine($"Object is now a: {typeof(T)}:");
};
var application = typeof(Program);
application
.GetMethod(nameof(UseAs), BindingFlags.Static | BindingFlags.NonPublic)
.MakeGenericMethod(asType)
.Invoke(null, new object[] { data });
}
不幸的是,GetMethod 代碼不再有效。 我聽說在編譯時編譯器會將任何本地函數轉換為靜態方法,所以我彈出到即時窗口並運行:
application.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)
......而且,我確實看到了這個回應:
{System.Reflection.MethodInfo[3]}
[0]: {Void Main(System.String[])}
[1]: {Void HandleResponse(System.Object, System.Type)}
[2]: {Void <HandleResponse>g__useAs1_0[T](T)}
這是列表中的最后一個方法。 有沒有人知道如何以合理的方式訪問這樣的方法?
謝謝!
編輯:
我確實可以將 UseAs 用作普通的私有靜態方法。 它不會在其他任何地方使用,所以我想將它全部“打包”在一種方法中。
此外,這實際上應該是一個關於查找本地函數的問題,並且在 StackOverflow 上的其他任何地方似乎都沒有關於它的問題。 我發現很難相信在某些時候有人至少不會對此感到好奇。
我一開始就猶豫是否提供任何代碼,因為我只是在修補一個想法,但我試圖實現的實際目標完全是問題的次要目標。
好的,我有辦法了。 但這真的很可怕。 它涉及從具有特定類型的方法創建委托,然后使用它來查找泛型方法,然后構造另一個特定方法並調用它。
所以我們從UseAs<int>
到UseAs<T>
再到UseAs<the-type-we-want>
。
它可能會在很多方面出現嚴重錯誤,但它適用於我測試過的非常有限的樣本:
// DISCLAIMER: THIS CODE IS FAIRLY HACKY, AND MAY WELL FAIL IN WEIRD
// SITUATIONS. USE WITH EXTREME CAUTION AND LOTS OF TESTS!
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
HandleResponse("foo", typeof(string));
}
static void HandleResponse(object data, Type type)
{
string local = "This was a local variable";
void UseAs<T>(T obj)
{
Console.WriteLine($"Object is now a: {typeof(T)}:");
// Proof that we're capturing the target too
Console.WriteLine($"Local was {local}");
}
InvokeHelper(UseAs, data, type);
}
// This could be in any class you want
static void InvokeHelper(Action<int> int32Action, object data, Type type)
{
// You probably want to validate that it really is a generic method...
var method = int32Action.Method;
var genericMethod = method.GetGenericMethodDefinition();
var concreteMethod = genericMethod.MakeGenericMethod(new[] { type });
concreteMethod.Invoke(int32Action.Target, new[] { data });
}
}
用反射調用本地函數就像找麻煩。 這個名字不是“固定的”。 它根據同一個類中有多少其他本地函數而改變......因此,如果您修改另一個方法,您可以更改您感興趣的本地函數的名稱。
你可以看看這個TryRoslyn 。
有三個類, Class1
, Class2
和Class3
。 它們都有一個方法M
,該方法內部有一個本地函數Test
。 Class1
和Class2
與最后一個字符相同。 然后將本地方法編譯為名為<M>g__Test0_0()
。 Class3
在M
方法之前引入了另一個方法Filler
,以及另一個本地函數 ( Foo
),然后將其編譯為<Filler>g__Foo0_0
。 在這種情況下, M
的本地方法名為<M>g__Test1_0()
。
我現在知道這是一個老問題 - 但我試圖理解為什么 HandleResponse 本身不能成為通用方法? 您可以將類型傳遞給本地函數,它本身可以使用相同的外部類型。 這消除了反射的需要。 這意味着每次使用不同類型的調用都會生成新方法 - 但我不知道你如何避免編譯和執行代碼中的這一步 - 你作為 UseAs 包含的方法是通用的。 所以這就是我的想法。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class testy
{
class a { }
class b : a { }
[TestMethod]
public void caller()
{
HandleResponse<a>(new b());
}
private static void HandleResponse<T>(object data)
{
UseAs((T)data);
void UseAs(T obj)
{
System.Diagnostics.Debug.WriteLine($"Object is now a: {typeof(T)}:");
}
}
}
我假設您實際上是在嘗試將一種類型轉換為另一種有效類型。 在這種情況下,您的代碼將不是類型安全的,並且可能會因“obj 無法轉換為 DateTime 類型”之類的內容而失敗。 我的解決方案沒有什么不同。 我假設您可以將對象強制轉換為 T。我不會檢查它是否可以轉換為該類型(在這種情況下有效使用反射)。 所以我相信我的小解決方案在功能上是等效的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.