8

質問全体を読んでください。いくつかの制約を解決したいという独特の状況があります。

私のコードには、にコンパイルされた式ツリーがありPredicate<System.Type>ます。私の目標は、アセンブリをロックせずにロードし (プロジェクトの出力アセンブリであり、常に再構築されます)、その型のリストにこの述語を適用し、結果の型名のリストを取得することです。

// this is what I need:
return assembly.GetTypes().Where(t => predicate(t)).Select(t => t.FullName);

必要な情報を取得したらすぐにアンロードしたいので、このアセンブリは別のアプリケーション ドメインにロードする必要があります。

ここがややこしいところです。私が直面しているいくつかの問題があります:

アセンブリを別のアプリドメインにロードし、そのすべての型の配列を返すだけで、述語をメインのアプリドメインに適用できるようにすると、型がメインのアプリドメインにマーシャリングされるとすぐに、次のようになりますFileNotFoundException。このアセンブリが見つかりません。アセンブリは、私が作成した別の appdomain にのみ読み込まれるため、これは理にかなっています。メインのアプリドメインにもロードすると、目的が無効になります。

または、述語を他のアプリケーション ドメインに渡してそこに適用し、文字列の配列 (完全な型名) を取得しようとするSerializationException: "Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator's assembly."と、述語が動的メソッド (式ツリーからコンパイル) であるため、が返されます。 .

プライマリ appdomain にロードすると、この問題は発生しませんが、appdomain 全体をアンロードせずにロード済みのアセンブリをアンロードすることは不可能であるため、(再構築後に) アセンブリが変更されるとすぐに、同じ名前のアセンブリをロードしようとすると、例外が発生します。

コンテキスト: Agent Mulder
という ReSharper のプラグインを作成しています。プラグインの背後にあるアイデアは、ソリューション内の DI/IoC コンテナーの登録を分析し、ReSharper が DI コンテナーを介して登録された型の使用法を把握できるようにすることです (仕組みの短いビデオをここで見ることができます)。

ほとんどの場合、コンテナー登録の分析は簡単です。どの具象型が影響を受けるかを知るのに十分な情報を検出するだけで済みます。Castle Windsor を使用したこの例でComponent.For<IFoo>().ImplementedBy<Foo>()は、結果の型は明らかですAllTypes.FromThisAssembly().BasedOn<IFoo>()。つまり、この行によって影響を受ける具体的な型を推定するのに十分な情報が得られます。ただし、Castle Windsor での次の登録を検討してください。

container.Register(Classes
    .FromAssemblyInDirectory(new AssemblyFilter(".").FilterByName(an => an.Name.StartsWith("Ploeh.Samples.Booking")))
    .Where(t => !(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dispatcher<>)))
    .WithServiceAllInterfaces());

ソース

ここでの情報は、実行時にのみ評価される述語に依存します。

私にできることはこれを静的に分析することだけなので、Where節からのラムダ式の ReSharper の AST (ReSharper では PSI と呼ばれる) 表現を手にしています。この AST を LINQ 式ツリーに変換し、デリゲートにコンパイルできます。

私の考えは、FromAssembly*リフレクションを介して出力アセンブリ (記述子によって決定される) をロードし、このデリゲートをアセンブリの型に適用して、型名を取得することでした (名前のみが必要です)。これも、アセンブリが変更されるたびに再評価する必要があります (この時点では、パフォーマンスについては心配していません)。

結論として、誰かが述語の影響を受ける型を判断するためのより良い方法を推奨できない限り、リフレクションでこれを行う方法を知りたいです (残念ながら、他のメタデータ リーダーを考慮していませんでした。ラムダ式 AST を異なるデータ型の述語に変換する必要があり、1 対 1 の変換が存在するかどうかはわかりません)。

読んでくれてありがとう。この質問が利用可能になると、500 ポイントの報奨金が与えられます。

4

2 に答える 2

2

Typeインスタンスを取得するにはアセンブリをロードする必要があるため、別の方法AppDomainが適切なソリューションのようです。

Expressionしたがって、その述語を取得する必要があります。つまりAppDomain、シリアル化/逆シリアル化する必要があります。

この要件は、さまざまな理由でますます頻繁になっています。WCF サービス全体で Linq to Entities 式を噴出したかったので、これを見ていました。

幸いなことに、既存の実装がいくつかあります。

私はこれを見つけました:CodePlex - Expression Tree Serializer


でテストしたところTypes、これは機能します:

Expression<Func<Type,bool>> predicate =
  t => ( !t.IsGenericType && t.Name == "Int32" );

var s = new ExpressionSerialization.ExpressionSerializer();
var xml = s.Serialize( predicate );

var p = s.Deserialize( xml ) as Expression<Func<Type, bool>>;
var f = p.Compile();

Console.WriteLine( "Int32: " + f( typeof( int ) ) ); // true
Console.WriteLine( "String: " + f( typeof( string ) ) ); // false
于 2012-05-07T11:50:35.393 に答える
1

述語デリゲート インスタンスを MBR オブジェクトでラップします。タイプのパラメーターはType、他のドメインから適切にマーシャリングされます。

class RemotablePredicate<T> : MarshalByRefObject
{
  readonly Predicate<T> predicate;
  public RemotablePredicate(Predicate<T> predicate) { this.predicate = predicate; }
  public bool Accepts(T arg) { return predicate(arg); }
}

いくつかのタイプをビルドしてロードし、アセンブリを調べて、結果をメイン ドメインに返します。

class AssemblyExplorer : MarshalByRefObject
{
  public string[] GetTypesByPredicate(
    string assemblyPath, RemotablePredicate<Type> predicate)
  {
    // MS reflection api reqires all dependencies here
    var bytes = File.ReadAllBytes(assemblyPath);
    var assembly = Assembly.ReflectionOnlyLoad(bytes);

    var types = new List<string>();
    foreach (var type in assembly.GetTypes())
      if (predicate.Accepts(type))
        types.Add(type.FullName);

    return types.ToArray();
  }
}

このすべてを機能させます。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;

class Program
{
  static void Main()
  {
    var fooDomain = AppDomain.CreateDomain("Foo");

    Expression<Predicate<Type>> expr = t => t.IsValueType;
    var compiledPredicate = expr.Compile();
    var remotablePredicate = new RemotablePredicate<Type>(compiledPredicate);

    var explorerType = typeof(AssemblyExplorer);
    var explorerInstance = (AssemblyExplorer) fooDomain
      .CreateInstanceAndUnwrap(explorerType.Assembly.FullName, explorerType.FullName);

    var types = explorerInstance.GetTypesByPredicate(
      "JetBrains.Annotations.dll", remotablePredicate);

    Console.WriteLine("Matched types: {0}", types.Length);
    foreach (var type in types) Console.WriteLine(" {0}", type);
  }
}
于 2012-05-07T13:20:02.093 に答える