Я создаю многоразовую библиотеку для Silverlight. Библиотека содержит внутренний универсальный тип, и мне нужно создать новый экземпляр этого универсального типа, но в какой-то момент у меня нет доступного аргумента универсального типа, только объект System.Type, представляющий универсальный аргумент. Я попытался создать экземпляр с помощью отражения, но это не удалось, потому что этот класс является внутренним, а Silverlight эффективно работает с частичным доверием.

Вот что я пробовал до сих пор:

private INonGenericInterface CreateInstance(Type type)
{
    // Activator.CreateInstance fails
    var instance = Activator.CreateInstance(
            typeof(InternalGenericType<>).MakeGenericType(type));

    // Invoking the default constructor of that type fails.
    var producer = typeof(InternalGenericType<>)
        .MakeGenericType(type)
        .GetConstructor(new Type[0])
        .Invoke(null);

    return (INonGenericInterface)producer;
}

Это мой внутренний тип. Ничего фантастического:

internal class InternalGenericType<T> : INonGenericInterface
    where T : class
{
    public InternalGenericType()
    {
    }
}

Я даже попытался злоупотребить структурой Nullable<T> как фабрикой для создания фабрики, которая могла бы производить мой внутренний тип. Однако по умолчанию Nullable<T> преобразуются в пустые ссылки:

internal static class InternalGenericTypeFactory
{
   public static INonGenericInterface Create(Type serviceType)
   {
      var nullType = typeof(Nullable<>).MakeGenericType(
         typeof(Factory<>).MakeGenericType(serviceType));

      // Activator succesfully creates the instance, but .NET
      // automatically converts default Nullable<T>s to null.
      object nullInstance = Activator.CreateInstance(nullType);

      var getValueMethod =
         nullType.GetMethod("GetValueOrDefault", new Type[0]);

      // Invoke fails, because nullInstance is a null ref.
      var factory = getValueMethod.Invoke(nullInstance, null);

      return ((IFactory)factory).CreateInstance();
   }

   internal interface IFactory
   {
      INonGenericInterface CreateInstance();
   }

   internal struct Factory<T> : IFactory where T : class
   {
       public INonGenericInterface CreateInstance()
       {
           return new InternalGenericType<T>();
       }
   }
}

Как вы понимаете, я не хочу делать этот тип общедоступным, потому что он загрязнит мой API. У меня сейчас нет идей. Какие у меня варианты? Что я могу сделать, чтобы создать этот внутренний тип?

2
Steven 29 Мар 2011 в 22:29

2 ответа

Лучший ответ

Третья альтернатива - поддержка некоего фабричного шаблона, который будет содержать метод для создания экземпляра внутреннего типа. И вы можете выставить factory или сделать общедоступным тип factory.

public class TypeFactory
{
    public static object Create<T>()
    {
         return new MyInternalType<T>();
    }
}

Вы можете оставить класс внутренним и вызвать метод TypeFactory через отражение.

public object CreateType(System.Type type)
{
    Type typeFactory = typeof(TypeFactory);
    MethodInfo m = typeFactory.GetMethod("Create").MakeGenericMethod(type);
    return m.Invoke(null, null);
}

Я думаю, что ваш TypeFactory должен быть публичным, он не может быть внутренним.

4
Steven 31 Мар 2011 в 10:03
Я не уверен, что понимаю тебя. Вы можете привести пример?
 – 
Steven
29 Мар 2011 в 22:51
@Akash: Спасибо за пример, но это, к сожалению, не поможет, потому что у меня нет общего T, только System.Type, представляющий этот T.
 – 
Steven
29 Мар 2011 в 23:38
Откуда взялась буква Т? Разве вы не можете поставить фабричного делегата или что-то вместо него / рядом с ним?
 – 
Lasse V. Karlsen
29 Мар 2011 в 23:56
@Lasse: Это вопрос к @Akash или ко мне? Если это для меня; Я не уверен, что понимаю тебя. Вы можете уточнить?
 – 
Steven
30 Мар 2011 в 10:24
@Steven, вам нужно будет использовать отражение для вызова этого метода, я показал вам пример.
 – 
Akash Kava
30 Мар 2011 в 11:51

У вас есть два варианта:

  1. Сделайте тип общедоступным
  2. Избегайте использования отражения для этого, используйте вместо этого универсальные шаблоны.

Если бы защитных мер можно было избежать только потому, что они вам не нравились, в них вообще не было бы необходимости.

3
Lasse V. Karlsen 29 Мар 2011 в 22:33
Спасибо за ваш ответ. Я не пытаюсь избежать механизма безопасности Silverlight, не волнуйтесь :-). Я боялся, что это были мои варианты.
 – 
Steven
29 Мар 2011 в 22:36
Приносим извинения, если это прозвучало так, как я предполагал, но да, это ваш выбор из-за системы безопасности в Silverlight. Я предполагаю, что создание в Silverlight приложения вне браузера, работающего с полным доверием, не вариант?
 – 
Lasse V. Karlsen
29 Мар 2011 в 22:38
Полное доверие - это не вариант. Это многоразовая библиотека, и ограничение моих пользователей запускать ее с полным доверием - это просто слишком большое ограничение.
 – 
Steven
29 Мар 2011 в 22:41