Какой наименее инвазивный способ сделать C # код поздно в .NET <4?

StackOverflow https://stackoverflow.com//questions/9670466

  •  12-12-2019
  •  | 
  •  

Вопрос

Я работаю над кусочком C # код, который имеет дело с элементами управления Windows Forms. Вот небольшой пример, крошечная обертка для получения ограничивающего прямоугольника (в координатах экрана) для некоторого управления:

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 Forms загружена в текущий домен приложений. С помощью .NET 4 я мог бы использовать ключевое слово dynamic, но ALAS, этот код должен работать с приложениями .NET3. Следовательно, я начал использовать API отражения, представляя маленький помощник объекта, который делает использование API отражения немного приятнее:

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;
        }
    }
}
.

Не очень красиво. Много литья, необходимого на стороне абонента, и я подозреваю, что я должен иметь дело с перегруженными методами или недвижимостью рано или поздно - довольно ухабистая поездка вперед. В идеале объект обертки позволит сохранить исходный код очень одинаковым.

Итак, прежде чем начать фиксацию класса обертки LateBoundObject up Интересно: у кого-нибудь еще есть опыт работы с C # код поздно, используя API отражения? Если это так, то, как вы подходили к нему, чтобы сохранить боль в использовании API необработанного отражения до минимума - также использовали класс обертки вдоль строк LateBoundObject или вы проходили совершенно другой маршрут? Я ищу наименее инвазивный путь, насколько оригинальный код обеспокоен.

Это было полезно?

Решение

Одной из представлений будет создание интерфейсов для того, что вы хотите, чтобы объекты были похоже на использование System.reflection.emit для генерации классов, которые могут Coerece реальный экземпляр. Вы можете сделать это, упаковывая его в динамически сгенерированный объект, который прокси вызовывают методы интерфейса к фактическому экземпляру, это обертывание.

Использование будет выглядеть что-то подобное:

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);
.

У меня есть блог здесь с простой отправной точкой для того, как сделать динамическое принуждение, включая приложение приложения: Типы принуждения и разгрузки узлов

Что интересно, в том, что с этой техникой вы можете фактически избавиться от отражения на вызове, что очень дорогое выступление. Вместо этого вы выполняете отражение один раз в прокси-генераторе и то, что вы генерируете, фактически вызывает соответствующее свойство / метод / поле непосредственно после этого. Также с этим трюком генерируется динамическая сборка, будет выгружена при отложении ссылки на экземпляр прокси. Вы можете генерировать тип типа, чтобы сделать последующие прокси-кредиты очень быстро.

Ваша ситуация сложнее, чем мой маленький образец, но я думаю, что вы можете получить очень далеко с ним в качестве отправной точки.

Другие советы

Я не понимаю.Я прохожу .NET 4 элементы управления DLL, скомпилированными против .NET 2, и они работают просто хорошо.

Используйте расширения хелпера для отражения:

 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);
  }
}
.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top