Могу ли я объявить переменную типа <T>без указания T во время компиляции? - PullRequest
5 голосов
/ 15 января 2010

Как загрузить класс «MyContent» динамически? У меня есть 1 interface<T>, 1 абстрактный универсальный class<T> и 1 класс. Проверьте мой код:

public interface IMyObjectInterface{
}
public abstract MyAbstractObject : IMyObjectInterface{
}
public class MyObject : MyAbstractObject{
}

public interface IMyContentInterface<T>  where T : MyAbstractObject
{
  void MyMethod();
}
public abstract MyAbstractContent<T>, IMyContentInterface<T>  where T : MyAbstractObject
{
  public abstract void MyMethod();
}
public public class MyContent : MyAbstractContent<MyObject>
{
  public override void MyMethod() { //do something }
}

Я пытаюсь, но, очевидно, это не работает:

IMyObjectInterface obj = (IMyObjectInterface)Assembly.Load("MyAssembly").CreateInstance("MyObject");
IMyContentInterface<obj> content = (IMyContentInterface<obj>)Assembly.Load("MyAssembly").CreateInstance("MyContent");
content.MyMethod();
//assembly and type names are correct

Если я изменю IMyContentInterface<obj> на IMyContentInterface<MyObject>, работает:

IMyContentInterface<MyObject> content = (IMyContentInterface<MyObject>)Assembly.Load("MyAssembly").CreateInstance("MyContent");
content.MyMethod();
//assembly and type names are correct

Проблема в том, что при определении IMyContentInterface<T> я не знаю, что будет моим объектом во 2-й строке. Пожалуйста, кто-нибудь знает, как это сделать в .NET Framework 4.0?

Ответы [ 4 ]

7 голосов
/ 15 января 2010

элемент в < > должен быть типом, а не объектом.

моя машина - объект типа машины, поэтому

Car myCar=new Car();

Мне нужен список, в котором будут храниться мои автомобили (объекты типа Car).

List<Car> myCars = new List<Car>();

А затем мы добавляем объект типа Car в мой список.

 myCars.Add(myCar);
 myCars.Add(anotherCar);
5 голосов
/ 15 января 2010

Я должен был прочитать это несколько раз, но я понял, о чем ты спрашиваешь. :) Этот вопрос является частным случаем этого другого вопроса:

Тем не менее, вот пример того, как вы можете использовать его для своего теста. Очевидно, вы можете изменить это. Кроме того, не пропустите мою последнюю заметку в конце этого ответа.

Assembly MyCompany.MyProduct.MyComponent:

Определите ваши интерфейсы в этой сборке:

namespace MyCompany.MyProduct.MyComponent
{
    public interface IMyObjectInterface
    {
        void MyObjectMethod();
    }

    /* It's important to include this non-generic interface as a base for
     * IMyContentInterface<T> because you will be able to reference this
     * in the assembly where you load components dynamically.
     */
    public interface IMyContentInterface
    {
        Type ObjectType
        {
            get;
        }

        void MyContentMethod();
    }

    public interface IMyContentInterface<T> : IMyContentInterface
        where T : IMyObjectInterface
    {
    }
}

Assembly MyCompany.MyProduct.MyComponent.Implementation:

Реализация интерфейсов в этой сборке, которые будут динамически загружаться.

namespace MyCompany.MyProduct.MyComponent
{
    public abstract class MyAbstractObject : IMyObjectInterface
    {
        public abstract void MyObjectMethod();
    }

    public class MyObject : MyAbstractObject
    {
        public override void MyObjectMethod() { }
    }

    public abstract class MyAbstractContent<T> : IMyContentInterface<T>
        where T : MyAbstractObject
    {
        public Type ObjectType
        {
            get
            {
                return typeof(T);
            }
        }

        public abstract void MyContentMethod();
    }

    public class MyContent : MyAbstractContent<MyObject>
    {
        public override void MyContentMethod() { }
    }
}

Сборка MyCompany.MyProduct

Ваша программа состоит из в этой сборке, термин, который я извлек из Managed Extensibility Framework . Эта сборка ссылается на MyCompany.MyProduct.MyComponent, но не MyCompany.MyProduct.MyComponent.Implementation в предположении, что интерфейсы с большей вероятностью останутся совместимыми , чем реализации во время разработки продукта. Этот дизайн является попыткой отдать предпочтение сплоченности по сравнению с (парой часто неправильно понятых слов), но фактические реализации, как правило, сильно различаются по успешности достижения этой цели.

namespace MyCompany.MyProduct
{
    using MyCompany.MyProduct.MyComponent;
    using System.Reflection;
    using System.Security.Policy;

    public class ComponentHost
    {
        public void LoadComponents()
        {
            Assembly implementation = LoadImplementationAssembly();

            /* The implementation assembly path might be loaded from an XML or
             * similar configuration file
             */
            Type objectType = implementation.GetType("MyCompany.MyProduct.MyComponent.MyObject");
            Type contentType = implementation.GetType("MyCompany.MyProduct.MyComponent.MyContent");

            /* THIS assembly only works with IMyContentInterface (not generic),
             * but inside the implementation assembly, you can use the generic
             * type since you can reference generic type parameter in the source.
             */
            IMyContentInterface content = (IMyContentInterface)Activator.CreateInstance(contentType);
        }

        private Assembly LoadImplementationAssembly()
        {
            /* The implementation assembly path might be loaded from an XML or
             * similar configuration file
             */
            string assemblyPath = "MyCompany.MyProduct.MyComponent.Implementation.dll";
            return Assembly.LoadFile(assemblyPath);
        }
    }
}

Заключительная нота:

Платформа Managed Extensibility Framework была создана как общее решение проблемы, над которой вы работаете. Проработав некоторое время, я с уверенностью говорю, что он обладает следующими хорошими свойствами:

  • Относительно короткая кривая обучения.
  • В результате получился очень чистый код.
  • Низкие затраты времени выполнения (сборка небольшая, производительность неплохая).

Я бы легко порекомендовал его как серьезный жизнеспособный вариант для тех, кто работает над новым приложением, если оно удовлетворяет любой комбинации из одного или нескольких из следующих:

  • Приложение разделено на компоненты (как было бы почти любое нетривиальное приложение).
  • Приложение должно быть гибким или расширяемым в будущем (как любой долгосрочный проект).
  • Приложение должно динамически загружать реализацию из неизвестной сборки.
5 голосов
/ 15 января 2010

Как загрузить класс «MyContent» динамически?

Загрузка это не сложно - вы уже знаете, как это сделать, но дженерики C # строго типизированы, проверены и гарантированы во время компиляции. Рассмотрим этот код:

List<string> list = new List<string>(); 
list.Add(new TcpSocket()); // This line won't compile

Компилятор C # не может сказать вам, что это незаконно, если вам было разрешено объявлять дженерики следующим образом:

Type type = GetTypeFromReflectedAssembly();
List<type> list = new List<type>();

// This *might* work - who knows?
list.Add(new TcpSocket());

Если ваша конечная цель - вызвать MyContent.MyMethod(), и это не имеет ничего общего с параметром универсального типа <T>, рассмотрите возможность объявления неуниверсального интерфейса, который вы можете реализовать где-нибудь в иерархии наследования, и объявите свой экземпляр переменная, использующая это:

IMyContentInterface content = (IMyContentInterface)Assembly.Load("MyAssembly").CreateInstance("MyContent");
content.MyMethod();
1 голос
/ 15 января 2010

Это способ динамической загрузки интерфейса. Это предполагает, что у вас есть какой-то способ получить сборку, из которой вы пытаетесь загрузить ее, и строку для имени типа.

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

ISomeInterface myInterface = this.GetComponent<ISomeInterface>("SomeImplementation");


public T GetComponent<T>(string componentName)
{
    // A method to dymanicly load a .dll, not shown in this example
    Assembly assembly = this.GetComponentAssembly(componentName);

    // A method to get a string assembly type, in this case from another source
    string assemblyType = this.GetAssemblyType(componentName);

    T component = (T)assembly.CreateInstance(assemblyType);

    return component;
}
...