C#メソッドのコンテンツを動的に置き換えますか?


108

私がやりたいのは、C#メソッドが呼び出されたときの実行方法を変更して、次のようなものを書けるようにすることです。

[Distributed]
public DTask<bool> Solve(int n, DEvent<bool> callback)
{
    for (int m = 2; m < n - 1; m += 1)
        if (m % n == 0)
            return false;
    return true;
}

実行時に、Distributed属性を持つメソッド(既に実行可能)を分析し、関数の本体が実行される前と関数が戻った後にコードを挿入できるようにする必要があります。さらに重要なことに、Solveが呼び出されるコードを変更することなく、または関数の開始時に(コンパイル時に、実行時に実行することが目的です)コードを変更せずにそれを実行できるようにする必要があります。

現時点で私はこのコードを試してみました(tはSolveが格納されている型であり、mはSolveのMethodInfoであると想定しています)

private void WrapMethod(Type t, MethodInfo m)
{
    // Generate ILasm for delegate.
    byte[] il = typeof(Dpm).GetMethod("ReplacedSolve").GetMethodBody().GetILAsByteArray();

    // Pin the bytes in the garbage collection.
    GCHandle h = GCHandle.Alloc((object)il, GCHandleType.Pinned);
    IntPtr addr = h.AddrOfPinnedObject();
    int size = il.Length;

    // Swap the method.
    MethodRental.SwapMethodBody(t, m.MetadataToken, addr, size, MethodRental.JitImmediate);
}

public DTask<bool> ReplacedSolve(int n, DEvent<bool> callback)
{
    Console.WriteLine("This was executed instead!");
    return true;
}

ただし、MethodRental.SwapMethodBodyは動的モジュールでのみ機能します。すでにコンパイルされ、アセンブリに格納されているものではありません。

したがって、読み込まれて実行中のアセンブリに既に格納されているメソッドで SwapMethodBodyを効果的に実行する方法を探しています。

メソッドを動的モジュールに完全にコピーする必要がある場合は問題ありませんが、この場合、IL全体にコピーする方法を見つけ、Solve()へのすべての呼び出しを更新して、新しいコピーを指します。


3
すでにロードされているメソッドを交換することはできません。そうでなければ、Spring.Netはプロキシとインターフェースで奇妙なことをする必要はありません:-)この質問を読んでください、それはあなたの問題に接しています:stackoverflow.com/questions/25803 / ... -それを交換してください...あなたが1できない場合は、明らかに2)できません。
xanatos

その場合、メソッドを動的モジュールにコピーし、そのメソッドの呼び出しが新しいコピーを指すように残りのアセンブリを更新する方法はありますか?
6月のロードス、

同じ古い同じ古い。それが簡単にできれば、さまざまなIoCコンテナすべてがおそらくそれを行うでしょう。彼らはそれをしません-> 99%それはできません:-)(恐ろしくて無敵のハックなしでは)。単一の希望があります。彼らはC#5.0でメタプログラミングと非同期を約束しました。私たちが見てきた非同期...メタプログラミングは何も...しかし、それはそれかもしれません!
xanatos

1
あなたは本当にあなたがなぜそんなに苦しいことのために自分を入れたいのか説明していません。
DanielOfTaebl

6
以下の私の答えを見てください。これは完全に可能です。所有していないコードと実行時。多くの人がこれは不可能だと思う理由がわかりません。
Andreas Pardeike 2017年

回答:


201

開示:Harmonyは、この投稿の作成者である私が作成し、管理しているライブラリーです。

Harmony 2は、実行時にあらゆる種類の既存のC#メソッドを置換、装飾、または変更するように設計されたオープンソースライブラリ(MITライセンス)です。主な焦点は、Monoまたは.NETで記述されたゲームとプラグインです。同じメソッドへの複数の変更を処理します-それらは互いに上書きする代わりに蓄積されます。

元のメソッドごとに動的な置換メソッドを作成し、最初と最後にカスタムメソッドを呼び出すコードをそれらに発行します。また、元のILコードとカスタム例外ハンドラーを処理するフィルターを記述して、元のメソッドをより詳細に操作することもできます。

プロセスを完了するには、動的メソッドのコンパイルから生成されたアセンブラを指す、元のメソッドのトランポリンに簡単なアセンブラジャンプを書き込みます。これは、Windows、macOS、およびMonoがサポートするLinux上の32/64ビットで機能します。

ドキュメントを見つけることができます ここにあります

出典

元のコード

public class SomeGameClass
{
    private bool isRunning;
    private int counter;

    private int DoSomething()
    {
        if (isRunning)
        {
            counter++;
            return counter * 10;
        }
    }
}

Harmonyアノテーションによるパッチ

using SomeGame;
using HarmonyLib;

public class MyPatcher
{
    // make sure DoPatching() is called at start either by
    // the mod loader or by your injector

    public static void DoPatching()
    {
        var harmony = new Harmony("com.example.patch");
        harmony.PatchAll();
    }
}

[HarmonyPatch(typeof(SomeGameClass))]
[HarmonyPatch("DoSomething")]
class Patch01
{
    static FieldRef<SomeGameClass,bool> isRunningRef =
        AccessTools.FieldRefAccess<SomeGameClass, bool>("isRunning");

    static bool Prefix(SomeGameClass __instance, ref int ___counter)
    {
        isRunningRef(__instance) = true;
        if (___counter > 100)
            return false;
        ___counter = 0;
        return true;
    }

    static void Postfix(ref int __result)
    {
        __result *= 2;
    }
}

または、リフレクションを使用した手動パッチ

using SomeGame;
using HarmonyLib;

public class MyPatcher
{
    // make sure DoPatching() is called at start either by
    // the mod loader or by your injector

    public static void DoPatching()
    {
        var harmony = new Harmony("com.example.patch");

        var mOriginal = typeof(SomeGameClass).GetMethod("DoSomething", BindingFlags.Instance | BindingFlags.NonPublic);
        var mPrefix = typeof(MyPatcher).GetMethod("MyPrefix", BindingFlags.Static | BindingFlags.Public);
        var mPostfix = typeof(MyPatcher).GetMethod("MyPostfix", BindingFlags.Static | BindingFlags.Public);
        // add null checks here

        harmony.Patch(mOriginal, new HarmonyMethod(mPrefix), new HarmonyMethod(mPostfix));
    }

    public static void MyPrefix()
    {
        // ...
    }

    public static void MyPostfix()
    {
        // ...
    }
}

ソースコードを見て、とても面白かった!ジャンプを実行するために使用される特定の手順がどのように機能するかを(ここおよび/またはドキュメントで)説明できますMemory.WriteJumpか?
トム

部分的に私自身のコメントに答えるために:48 B8 <QWord>にQWORD即値を移動させrax、その後、FF E0あるjmp rax-オールクリアがあります!私の残りの質問は、E9 <DWord>ケース(ニアジャンプ)についてです。この場合、ニアジャンプは保持され、変更はジャンプのターゲット上にあります。Monoが最初にそのようなコードを生成するのはいつですか、なぜこの特別な扱いを受けるのですか?
トム

1
私の知る限り、まだ.NET Core 2をサポートしていないため、AppDomain.CurrentDomain.DefineDynamicAssemblyでいくつかの例外が発生します
Max

1
私の友人である0x0adeは、.NET Coreで機能する成熟度の低い代替策、つまりNuGetのMonoMod.RuntimeDetourがあることを私に話してくれました。
Andreas Pardeike

1
更新:System.Reflection.Emitへの参照を含めることで、Harmonyは.NET Core 3でコンパイルおよびテストできるようになりました
Andreas Pardeike

181

.NET 4以降の場合

using System;
using System.Reflection;
using System.Runtime.CompilerServices;


namespace InjectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Target targetInstance = new Target();

            targetInstance.test();

            Injection.install(1);
            Injection.install(2);
            Injection.install(3);
            Injection.install(4);

            targetInstance.test();

            Console.Read();
        }
    }

    public class Target
    {
        public void test()
        {
            targetMethod1();
            Console.WriteLine(targetMethod2());
            targetMethod3("Test");
            targetMethod4();
        }

        private void targetMethod1()
        {
            Console.WriteLine("Target.targetMethod1()");

        }

        private string targetMethod2()
        {
            Console.WriteLine("Target.targetMethod2()");
            return "Not injected 2";
        }

        public void targetMethod3(string text)
        {
            Console.WriteLine("Target.targetMethod3("+text+")");
        }

        private void targetMethod4()
        {
            Console.WriteLine("Target.targetMethod4()");
        }
    }

    public class Injection
    {        
        public static void install(int funcNum)
        {
            MethodInfo methodToReplace = typeof(Target).GetMethod("targetMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            MethodInfo methodToInject = typeof(Injection).GetMethod("injectionMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
#if DEBUG
                    Console.WriteLine("\nVersion x86 Debug\n");

                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;

                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x86 Release\n");
                    *tar = *inj;
#endif
                }
                else
                {

                    long* inj = (long*)methodToInject.MethodHandle.Value.ToPointer()+1;
                    long* tar = (long*)methodToReplace.MethodHandle.Value.ToPointer()+1;
#if DEBUG
                    Console.WriteLine("\nVersion x64 Debug\n");
                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;


                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x64 Release\n");
                    *tar = *inj;
#endif
                }
            }
        }

        private void injectionMethod1()
        {
            Console.WriteLine("Injection.injectionMethod1");
        }

        private string injectionMethod2()
        {
            Console.WriteLine("Injection.injectionMethod2");
            return "Injected 2";
        }

        private void injectionMethod3(string text)
        {
            Console.WriteLine("Injection.injectionMethod3 " + text);
        }

        private void injectionMethod4()
        {
            System.Diagnostics.Process.Start("calc");
        }
    }

}

14
これにはもっと多くの賛成票が必要です。私はまったく異なるシナリオを持っていますが、このスニペットは私を正しい方向に設定するために必要なものです。ありがとう。
SC

2
@Logman素晴らしい答え。しかし私の質問は:デバッグモードで何が起こっているのですか?また、1つの命令のみを置き換えることはできますか?たとえば、条件付きジャンプを無条件ジャンプに置き換えたい場合はどうすればよいですか?AFAIKはコンパイルされたメソッドを置き換えているため、どの条件を置き換える必要があるかを判断するのは簡単ではありません...
Alex Zhukovskiy

2
@AlexZhukovskiy(スタックに投稿してリンクを送信する場合)。週末に調べて答えます。マシン週末以降のご質問もお調べいたします。
Logman

2
MSTestを持つ統合テストのためにこれをやったときに私は気づいた二つのこと:(1)使用する場合thisの内部でinjectionMethod*()それが参照するInjection時にインスタンスをコンパイル時、しかしTarget中にインスタンスのランタイム(これはあなたが注入された内部で使用してインスタンスメンバーへのすべての参照のために真であります方法)。(2)何らかの理由で、#DEBUGパーツはテストのデバッグ時にのみ機能し、デバッグコンパイルされたテストの実行時には機能しませんでした。いつも#elseパーツを使ってしまいました。これが機能する理由はわかりませんが、機能します。
おやすみオタクプライド

2
非常に素晴らしい。すべてを壊す時間!@GoodNightNerdPride プリプロセッサのDebugger.IsAttached代わりに使用#if
M.kazem Akhgary

25

実行時にメソッドのコンテンツを変更できます。ただし、これは必須ではありません。テスト目的で保持することを強くお勧めします。

ちょうど見てください:

http://www.codeproject.com/Articles/463508/NET-CLR-Injection-Modify-IL-Code-during-Run-time

基本的に、次のことができます。

  1. MethodInfo.GetMethodBody()。GetILAsByteArray()を介してILメソッドのコンテンツを取得します
  2. これらのバイトを混乱させます。

    コードをプリペンドまたはアペンドするだけの場合は、必要なオペコードをプリプリンド/アペンドするだけです(ただし、スタックをクリーンな状態にしておくことに注意してください)。

    ここでは、既存のILを「アンコンパイル」するためのヒントをいくつか示します。

    • 返されるバイトは、一連のIL命令とそれに続く引数です(たとえば、「。call」には引数が1つあります:呼び出されたメソッドトークンがあり、「。pop」には引数がありません)。
    • ILコードと返された配列で見つかったバイトとの対応は、OpCodes.YourOpCode.Value(アセンブリに保存されている実際のopcodeバイト値)を使用して見つけることができます。
    • ILコードの後に​​追加される引数は、呼び出されるオペコードに応じて、異なるサイズ(1バイトから数バイト)になる場合があります
    • これらの引数が適切なメソッドを介して参照しているトークンを見つけることがあります。たとえば、ILに「.call 354354」(ヘキサでは28 00 05 68 32、28h = 40は「.call」オペコードおよび56832h = 354354としてコード化)が含まれている場合、対応する呼び出されたメソッドは、MethodBase.GetMethodFromHandle(354354を使用して見つけることができます。 )
  3. 変更したら、ILバイト配列をInjectionHelper.UpdateILCodes(MethodInfo method、byte [] ilCodes)で再注入できます-上記のリンクを参照してください

    これは「安全でない」部分です...うまく機能しますが、これは内部のCLRメカニズムをハッキングすることで構成されています...


7
念のため、354354(0x00056832)は有効なメタデータトークンではありません。上位バイトは0x06(MethodDef)、0x0A(MemberRef)または0x2B(MethodSpec)である必要があります。また、メタデータトークンはリトルエンディアンのバイト順で書き込む必要があります。最後に、メタデータトークンはモジュール固有であり、MethodInfo.MetadataTokenは宣言モジュールからトークンを返すため、変更中のメソッドと同じモジュールで定義されていないメソッドを呼び出す場合は、トークンを使用できません。
Brian Reichle、2015年

13

メソッドが非仮想、非ジェネリック、ジェネリックタイプではなく、インライン化されておらず、x86プレートフォーム上にある場合は、これを置き換えることができます。

MethodInfo methodToReplace = ...
RuntimeHelpers.PrepareMetod(methodToReplace.MethodHandle);

var getDynamicHandle = Delegate.CreateDelegate(Metadata<Func<DynamicMethod, RuntimeMethodHandle>>.Type, Metadata<DynamicMethod>.Type.GetMethod("GetMethodDescriptor", BindingFlags.Instance | BindingFlags.NonPublic)) as Func<DynamicMethod, RuntimeMethodHandle>;

var newMethod = new DynamicMethod(...);
var body = newMethod.GetILGenerator();
body.Emit(...) // do what you want.
body.Emit(OpCodes.jmp, methodToReplace);
body.Emit(OpCodes.ret);

var handle = getDynamicHandle(newMethod);
RuntimeHelpers.PrepareMethod(handle);

*((int*)new IntPtr(((int*)methodToReplace.MethodHandle.Value.ToPointer() + 2)).ToPointer()) = handle.GetFunctionPointer().ToInt32();

//all call on methodToReplace redirect to newMethod and methodToReplace is called in newMethod and you can continue to debug it, enjoy.

それは狂ったように危険です。誰もそれを量産コードで使用しないことを本当に望みます。
Brian Reichle、2015年

2
これは、アプリケーションパフォーマンス監視(APM)ツールで使用され、本番環境でも使用されます。
Martin Kersten、2015

1
返信ありがとうございます。アスペクト指向プログラミングAPIとしてこの種の機能を提供するプロジェクトに取り組んでいます。x86とx64の両方で仮想メソッドとジェネリックメソッドを管理する制限を解決しました。詳細が必要な場合はお知らせください。
Teter28、2015

6
クラスメタデータとは何ですか?
セバスチャン

この回答は疑似コードであり、古くなっています。メソッドの多くは存在しません。
N-ate

9

実行時に任意のメソッドを動的に変更できるいくつかのフレームワークが存在します(これらは、user152949によって言及されたICLRProfilingインターフェイスを使用します)。

.NETの内部を模倣するフレームワークもいくつかあります。これらはおそらく壊れやすく、インライン化されたコードを変更することはできませんが、一方で完全に自己完結型であり、使用する必要はありません。カスタムランチャー。

  • ハーモニー:MITライセンス。いくつかのゲームMODで実際に使用されているようで、.NETとMonoの両方をサポートしています。
  • Deviare In Process Instrumentation Engine:GPLv3およびCommercial。現在.NETのサポートは実験的としてマークされていますが、商業的に支援されているという利点があります。

8

Logmanのソリューションですが、メソッド本体を交換するためのインターフェースがあります。また、より簡単な例。

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace DynamicMojo
{
    class Program
    {
        static void Main(string[] args)
        {
            Animal kitty = new HouseCat();
            Animal lion = new Lion();
            var meow = typeof(HouseCat).GetMethod("Meow", BindingFlags.Instance | BindingFlags.NonPublic);
            var roar = typeof(Lion).GetMethod("Roar", BindingFlags.Instance | BindingFlags.NonPublic);

            Console.WriteLine("<==(Normal Run)==>");
            kitty.MakeNoise(); //HouseCat: Meow.
            lion.MakeNoise(); //Lion: Roar!

            Console.WriteLine("<==(Dynamic Mojo!)==>");
            DynamicMojo.SwapMethodBodies(meow, roar);
            kitty.MakeNoise(); //HouseCat: Roar!
            lion.MakeNoise(); //Lion: Meow.

            Console.WriteLine("<==(Normality Restored)==>");
            DynamicMojo.SwapMethodBodies(meow, roar);
            kitty.MakeNoise(); //HouseCat: Meow.
            lion.MakeNoise(); //Lion: Roar!

            Console.Read();
        }
    }

    public abstract class Animal
    {
        public void MakeNoise() => Console.WriteLine($"{this.GetType().Name}: {GetSound()}");

        protected abstract string GetSound();
    }

    public sealed class HouseCat : Animal
    {
        protected override string GetSound() => Meow();

        private string Meow() => "Meow.";
    }

    public sealed class Lion : Animal
    {
        protected override string GetSound() => Roar();

        private string Roar() => "Roar!";
    }

    public static class DynamicMojo
    {
        /// <summary>
        /// Swaps the function pointers for a and b, effectively swapping the method bodies.
        /// </summary>
        /// <exception cref="ArgumentException">
        /// a and b must have same signature
        /// </exception>
        /// <param name="a">Method to swap</param>
        /// <param name="b">Method to swap</param>
        public static void SwapMethodBodies(MethodInfo a, MethodInfo b)
        {
            if (!HasSameSignature(a, b))
            {
                throw new ArgumentException("a and b must have have same signature");
            }

            RuntimeHelpers.PrepareMethod(a.MethodHandle);
            RuntimeHelpers.PrepareMethod(b.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)b.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)a.MethodHandle.Value.ToPointer() + 2;

                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;

                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    int tmp = *tarSrc;
                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
                    *injSrc = (((int)tarInst + 5) + tmp) - ((int)injInst + 5);
                }
                else
                {
                    throw new NotImplementedException($"{nameof(SwapMethodBodies)} doesn't yet handle IntPtr size of {IntPtr.Size}");
                }
            }
        }

        private static bool HasSameSignature(MethodInfo a, MethodInfo b)
        {
            bool sameParams = !a.GetParameters().Any(x => !b.GetParameters().Any(y => x == y));
            bool sameReturnType = a.ReturnType == b.ReturnType;
            return sameParams && sameReturnType;
        }
    }
}

1
これにより、「System.AccessViolationException」タイプの例外がMA.ELCalc.FunctionalTests.dllで発生しましたが、ユーザーコードでは処理されませんでした追加情報:保護されたメモリの読み取りまたは書き込みを試みました。これは、他のメモリが破損していることを示していることがよくあります。
N-ate

「wapMethodBodiesはまだ8のIntPtrサイズを処理しない」という例外が発生しました
Phong Dao

6

この質問と別の質問への回答に基づいて、iveはこの整頓されたバージョンを考え出しました。

// Note: This method replaces methodToReplace with methodToInject
// Note: methodToInject will still remain pointing to the same location
public static unsafe MethodReplacementState Replace(this MethodInfo methodToReplace, MethodInfo methodToInject)
        {
//#if DEBUG
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
//#endif
            MethodReplacementState state;

            IntPtr tar = methodToReplace.MethodHandle.Value;
            if (!methodToReplace.IsVirtual)
                tar += 8;
            else
            {
                var index = (int)(((*(long*)tar) >> 32) & 0xFF);
                var classStart = *(IntPtr*)(methodToReplace.DeclaringType.TypeHandle.Value + (IntPtr.Size == 4 ? 40 : 64));
                tar = classStart + IntPtr.Size * index;
            }
            var inj = methodToInject.MethodHandle.Value + 8;
#if DEBUG
            tar = *(IntPtr*)tar + 1;
            inj = *(IntPtr*)inj + 1;
            state.Location = tar;
            state.OriginalValue = new IntPtr(*(int*)tar);

            *(int*)tar = *(int*)inj + (int)(long)inj - (int)(long)tar;
            return state;

#else
            state.Location = tar;
            state.OriginalValue = *(IntPtr*)tar;
            * (IntPtr*)tar = *(IntPtr*)inj;
            return state;
#endif
        }
    }

    public struct MethodReplacementState : IDisposable
    {
        internal IntPtr Location;
        internal IntPtr OriginalValue;
        public void Dispose()
        {
            this.Restore();
        }

        public unsafe void Restore()
        {
#if DEBUG
            *(int*)Location = (int)OriginalValue;
#else
            *(IntPtr*)Location = OriginalValue;
#endif
        }
    }

現時点ではこれが最良の答えです
ユージーンゴルボボイ

使用例を追加すると役立ちます
kofifus


3

私はそれがあなたの質問に対する正確な答えではないことを知っていますが、それを行う通常の方法は工場/プロキシアプローチを使用することです。

最初に基本型を宣言します。

public class SimpleClass
{
    public virtual DTask<bool> Solve(int n, DEvent<bool> callback)
    {
        for (int m = 2; m < n - 1; m += 1)
            if (m % n == 0)
                return false;
        return true;
    }
}

次に、派生型を宣言できます(プロキシと呼びます)。

public class DistributedClass
{
    public override DTask<bool> Solve(int n, DEvent<bool> callback)
    {
        CodeToExecuteBefore();
        return base.Slove(n, callback);
    }
}

// At runtime

MyClass myInstance;

if (distributed)
    myInstance = new DistributedClass();
else
    myInstance = new SimpleClass();

派生型は実行時にも生成できます。

public static class Distributeds
{
    private static readonly ConcurrentDictionary<Type, Type> pDistributedTypes = new ConcurrentDictionary<Type, Type>();

    public Type MakeDistributedType(Type type)
    {
        Type result;
        if (!pDistributedTypes.TryGetValue(type, out result))
        {
            if (there is at least one method that have [Distributed] attribute)
            {
                result = create a new dynamic type that inherits the specified type;
            }
            else
            {
                result = type;
            }

            pDistributedTypes[type] = result;
        }
        return result;
    }

    public T MakeDistributedInstance<T>()
        where T : class
    {
        Type type = MakeDistributedType(typeof(T));
        if (type != null)
        {
            // Instead of activator you can also register a constructor delegate generated at runtime if performances are important.
            return Activator.CreateInstance(type);
        }
        return null;
    }
}

// In your code...

MyClass myclass = Distributeds.MakeDistributedInstance<MyClass>();
myclass.Solve(...);

唯一のパフォーマンスの低下は、派生オブジェクトの構築中です。反射と反射放出を多く使用するため、初回は非常に遅くなります。それ以外の場合は、同時テーブル検索とコンストラクタのコストです。言ったように、あなたは建設を最適化することができます

ConcurrentDictionary<Type, Func<object>>.

1
うーん..それでも、分散処理に積極的に気づくためには、プログラマーのために作業が必要です。私は、メソッドの[Distributed]属性の設定にのみ依存する(ContextBoundObjectからのサブクラス化または継承ではない)ソリューションを探していました。Mono.Cecilなどを使用して、アセンブリのコンパイル後の変更を行う必要があるようです。
6月のロードス、

これは通常の方法だとは言いません。この方法は、必要なスキルの点では簡単です(CLRを理解する必要はありません)が、置換されたメソッド/クラスごとに同じ手順を繰り返す必要があります。後で何かを変更したい場合(たとえば、前だけでなく後でもコードを実行する場合)は、N回実行する必要があります(1度実行する必要がある安全でないコードとは対照的)。つまり、N時間の仕事と1時間の仕事です)
ユージーンゴルボヴォイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.