2012-03-12 10 views
2

私はWindowsフォームコントロールを扱うC#コードを扱っています。ここで小さな例では、いくつかのコントロールのために(スクリーン座標で)外接する四角形を取得するための小さなラッパーです:C#コードを.NET <4でレイトバインドするための最も侵略的でない方法は何ですか?

public class GUIObject { 
    protected Control m_control; 

    // [..] 

    public virtual Rectangle Bounds { 
     get { 
      Rectangle r = m_control.Bounds; 
      if (m_control.Parent != null) { 
       return m_control.Parent.RectangleToScreen(r); 
      } 
      return r; 
     } 
    } 
} 

このコードは、顧客にロードされるように、「プラグイン」として配布されたライブラリにコンパイルされますアプリケーション。しかし、一部の顧客は、プラグインがリンクされていたバージョンとは異なるバージョンのWindowsフォームをアプリケーションで使用していました。私の計画は、上記のコードを遅くすることでこれに取り組み、現在のアプリケーションドメインにロードされているどのWindowsフォームのバージョンでも動作するようにしました。 .NET 4では、キーワードdynamicを使用できましたが、残念ながら、このコードは.NET3アプリケーションでも動作するはずです。

public class LateBoundObject { 
    private Object m_o; 

    // [..] 

    public Object GetProperty(String name) { 
     PropertyInfo pi = m_o.GetType().GetProperty(name); 
     return pi == null ? null 
          : pi.GetValue(m_o, null); 
    } 

    public Object InvokeMethod(String name, Object[] args) { 
     MethodInfo mi = m_o.GetType().GetMethod(name); 
     return mi == null ? null 
          : mi.Invoke(m_o, args); 
    } 
} 

public class GUIObject { 
    protected LateBoundObject m_control; 

    // [..] 

    public virtual Rectangle Bounds { 
     get { 
      Object r = m_control.GetProperty("Bounds"); 
      if (r == null) { 
       return new Rectangle(); 
      } 

      Object parent = m_control.GetProperty("Parent"); 
      if (parent != null) { 
       LateBoundObject po = new LateBoundObject(parent); 
       r = po.InvokeMethod("RectangleToScreen", 
            new Object[] { r }); 
      } 
      return (Rectangle)r; 
     } 
    } 
} 

ない非常にきれい:したがって、私は、リフレクションAPIを少しよりよいを使用して作る小さなヘルパーオブジェクトを導入し、リフレクションAPIを使用して開始しました。呼び出し側では多くのキャストが必要で、遅かれ早かれオーバーロードされたメソッドやプロパティにも対処しなければならないと思います。理想的には、ラッパーオブジェクトは元のコードを非常に同じに保つことができます。

LateBoundObjectラッパークラスの修正を開始する前に、他の誰かがリフレクションAPIを使用してC#コードをレイトバインドする経験がありますか?もしそうなら、生のリフレクションAPIを最小限に抑えるという痛みを避けるためにどうやってアプローチしましたか?また、LateBoundObjectの行に沿ってラッパークラスを使用しましたか、まったく違うルートに行きましたか?私は、元のコードに関する限り、最も侵襲的な方法を探しています。

+0

いくつの「WinFormsバージョン」がいくつありますか?私はあなたがFx2.0用のlibをターゲットにし、Fx1用とFx4用の別々のものを作るべきだと思う。 –

+0

@HenkHolterman:Windows Formsの場合、プラグインを複数回構築するだけで十分だろう。しかし、私は他のツールキット(WPFや「拡張WPF」のような多くのサードパーティのツールキット)で同じ問題に直面しています。だから私は、私のコードを遅くすることで、何度も何度もビルドするのを避けることができるのだろうかと思います。 –

+0

あなたのFx4ユーザーにレイトバインドされたFx1.1コードを適用してもよろしいですか? –

答えて

1

ひとつのアイデアは、あなたが実際のインスタンスをcoereceできるクラスを生成するためにSystem.Reflection.Emitを使用するオブジェクトが見えるようにしたい何のためのインタフェースを作成することです。これは、動的に生成されたオブジェクトにラップすることによって行うことができます。このオブジェクトは、インターフェイスメソッドからラップしている実際のインスタンスに呼び出しを代行します。私はサンプルアプリケーションなどの動的な強制を、実行する方法についての簡単な出発点とここにブログの記事を持っている

interface IGUIObject 
{ 
    Rectangle Bounds { get; } 
    Rectangle RectangleToScreen(Rectangle bounds); 
    IGUIObject Parent { get; } 
} 

var obj = GetInstance(); 
var proxy = Reflection.Coerce<IGUIObject>(obj); 
return proxy.Parent.RectangleToScreen(proxy.Bounds); 

使い方は次のようになります興味深い何coercing types and unloading assemblies

は持つということですこの手法では、コールごとのリフレクションを実際に取り除くことができます。これは非常に高価なパフォーマンスです。その代わりに、プロキシジェネレータで一度だけリフレクションを行います。生成するものは、実際にそれに続いて、対応するプロパティ/メソッド/フィールドを実際に呼び出します。また、このトリックで生成された動的アセンブリは、プロキシインスタンスへの参照を削除するとアンロードされます。タイプ生成タイプをキャッシュして、後続のプロキシ作成を非常に高速にすることができます。

あなたの状況は私の小さなサンプルより複雑ですが、出発点として非常に遠くになるかもしれないと思います。

+0

非常に興味深い!私は確かに見ています。エミット施設について私はグーグルのことを聞いています。私はそれがここで役に立つかもしれないと思っていました。ポインタありがとう! –

1

私はそれを得ていません。私は.NET 4コントロールを.NET 2用にコンパイルされたDLLに渡し、うまく動作します。反射用

+0

問題は、顧客アプリケーションドメインに動的にロードされる私のアセンブリが、彼が使用しているものとは異なるサードパーティアセンブリのリンクとリンクしていることです。プラグインが作成されました。 –

+0

あなたの場合、私はロード時にコンパイルすることをお勧めします(プラグインがロードされ、結果のアセンブリをロードするときにcsc.exeを呼び出す)。 – Joshua

1

使用ヘルパー拡張:

var r = m_control._P<Rectangle>("Bounds") ?? new Rectangle(); 
var parent = m_control._P<Control>("Parent"); 
if (parent != null) 
    r = parent._M<Rectangle>("RectangleToScreen", r); 



static public class ReflectionHlp2 
{ 
    public static T _P<T>(this object item, string name) 
    { 
    if (item == null) 
     return default(T); 
    var type = item.GetType(); 

    var members = type.GetMembers(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) 
     .Where(_member => _member.Name == name) 
     .ToArray(); 
    if (members.Length == 0) 
     return default(T); 
    if (members.Length > 1) 
     throw new Exception(string.Format("У объекта полей/свойств с именем '{0}' больше чем один: '{1}'", name, members.Length)); 
    var member = members.First(); 
    object result; 
    if (member is FieldInfo) 
     result = ((FieldInfo)member).GetValue(item); 
    else 
     result = ((PropertyInfo)member).GetValue(item, null); 
    if (result is T) 
     return (T)result; 
    return default(T); 
    } 
    public static void _P<T>(this object item, string name, T value) 
    { 
    if (item == null) 
     return; 
    var type = item.GetType(); 

    var members = type.GetMembers(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) 
     .Where(_member => _member.Name == name) 
     .ToArray(); 
    if (members.Length == 0) 
     return; 
    if (members.Length > 1) 
     throw new Exception(string.Format("У объекта полей/свойств с именем '{0}' больше чем один: '{1}'", name, members.Length)); 
    var member = members.First(); 
    if (member is FieldInfo) 
     ((FieldInfo)member).SetValue(item, value); 
    else 
     ((PropertyInfo)member).SetValue(item, value, null); 
    } 
    public static void _M(this object item, string name, params object[] args) 
    { 
    _M<object>(item, name, args); 
    } 
    public static T _M<T>(this object item, string name, params object[] args) 
    { 
    if (item == null) 
     return default(T); 
    var type = item.GetType(); 

    var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) 
     .Where(_member => _member.Name == name) 
     .ToArray(); 
    if (methods.Length == 0) 
     return default(T); 
    if (methods.Length > 1) 
     throw new Exception(string.Format("Вызов перегруженных методов не поддерживается, у объекта методов с именем '{0}' больше чем один: '{1}'.", name, methods.Length)); 
    var method = methods.First(); 
    var result = method.Invoke(item, args); 
    if (result is T) 
     return (T)result; 
    return default(T); 
    } 
} 
+0

キリル文字例外メッセージの+1。 ;-) –

関連する問題