Confiança parcial:Como instanciar um tipo genérico sem um argumento de tipo genérico
-
14-11-2019 - |
Pergunta
Estou criando uma biblioteca reutilizável para o Silverlight.A biblioteca contém um tipo genérico interno e preciso criar uma nova instância desse tipo genérico, mas a certa altura não tenho um argumento de tipo genérico disponível, apenas um System.Type
objeto que representa o argumento genérico.Tentei criar uma instância usando reflexão, mas falhou, porque essa classe é interna e o Silverlight funciona efetivamente com confiança parcial.
Aqui está o que tentei até agora:
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;
}
Este é o meu tipo interno.Nada chique:
internal class InternalGenericType<T> : INonGenericInterface
where T : class
{
public InternalGenericType()
{
}
}
Eu até tentei abusar do Nullable<T>
struct como uma fábrica para criar uma fábrica que possa produzir meu tipo interno.No entanto, padrão Nullable<T>
seja convertido em referências nulas:
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>();
}
}
}
Como você pode imaginar, não quero tornar esse tipo público, pois poluiria minha API.Atualmente estou sem ideias.Quais são minhas opções?O que posso fazer para criar esse tipo interno?
Solução
A terceira alternativa é suportar algum tipo de padrão de fábrica que conterá um método para instanciar o tipo interno.E você pode expor a fábrica ou tornar público o tipo de fábrica.
public class TypeFactory
{
public static object Create<T>()
{
return new MyInternalType<T>();
}
}
Você pode deixar a classe como interna e invocar o método TypeFactory por meio de reflexão.
public object CreateType(System.Type type)
{
Type typeFactory = typeof(TypeFactory);
MethodInfo m = typeFactory.GetMethod("Create").MakeGenericMethod(type);
return m.Invoke(null, null);
}
Acho que seu TypeFactory deveria ser público, não pode ser interno.
Outras dicas
Você tem duas opções:
- Torne o tipo público
- Evite usar reflexão para fazer isso; em vez disso, use genéricos.
Se fosse possível evitar as salvaguardas só porque você não gosta delas, não haveria nenhuma necessidade de tê-las.