簡體   English   中英

思考:如何在 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

有三個類, Class1Class2Class3 它們都有一個方法M ,該方法內部有一個本地函數Test Class1Class2與最后一個字符相同。 然后將本地方法編譯為名為<M>g__Test0_0() Class3M方法之前引入了另一個方法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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM