tipos - metodos en c#
Instalar una clase con un constructor interno (8)
En caso de que alguien tropiece con esto de nuevo, aquí hay un ejemplo de cómo plantearlo a través de la reflexión:
var args = FormatterServices.GetUninitializedObject(typeof(SizeChangedEventArgs)) as SizeChangedEventArgs;
Debug.Assert(args != null);
var field = typeof(SizeChangedEventArgs).GetField("_previousSize", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, new Size(0,0));
field = typeof(SizeChangedEventArgs).GetField("_element", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, GraphicsWrapper);
field = typeof(RoutedEventArgs).GetField("_source", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, GraphicsWrapper);
field = typeof(RoutedEventArgs).GetField("_routedEvent", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, SizeChangedEvent);
GraphicsWrapper.RaiseEvent(args);
... donde GraphicsWrapper
es el control WPF del que desea que se eleve.
Tengo una clase cuyo constructor se define como interno, lo que significa que no puedo crear una instancia. Si bien eso puede tener sentido, todavía me gustaría hacerlo una vez para fines de depuración e investigación.
¿Es posible hacerlo con Reflection? Sé que puedo acceder a miembros privados / internos, pero ¿puedo llamar a un constructor interno?
O, como el constructor no hace nada importante, ¿puedo usar el reflejo para decir "Mira, solo dame una instancia de la clase sin llamar al constructor, lo haré manualmente"?
El rendimiento y la "estabilidad" no son un problema aquí, ya que no es un código de producción.
Editar: Solo como aclaración: Lamentablemente, no controlo el otro ensamblado y no tengo su código fuente, simplemente trato de entender cómo funciona, ya que su documentación es inexistente, pero se supone que tengo que interactuar con eso.
Este es un método derivado de esta respuesta :
public static T CreateInstance<T>(params object[] args)
{
var type = typeof (T);
var instance = type.Assembly.CreateInstance(
type.FullName, false,
BindingFlags.Instance | BindingFlags.NonPublic,
null, args, null, null);
return (T) instance;
}
Uso de ejemplo (este es un tipo de SDK Kinect que necesitaba crear para pruebas unitarias):
DiscreteGestureResult a = CreateInstance<DiscreteGestureResult>(false, false, 0.5f);
Existe un método FormatterServices.GetUninitializedObject (Namespace: System.Runtime.Serialization), supuestamente no llama a constructores, si realmente desea probar ese enfoque.
Experimenté esta misma situación hace un tiempo y creé una pequeña utilidad que llamé "InternalsVisibleToInjector". Utiliza ILDASM e ILASM para desmontar, modificar y volver a ensamblar y ensamblar con el nombre del ensamblaje de mi selección agregado a la lista InternalsVisibleTo para el ensamblaje de destino. Funcionó bastante bien en mi situación.
He publicado el código fuente (VS 2008 C # WinForm) para la utilidad aquí:
http://www.schematrix.com/downloads/InternalsVisibleToInjector.zip
Esto puede no funcionar si el ensamblado está firmado. Tome todas las precauciones adecuadas (es decir, realice una copia de seguridad del ensamblaje antes de utilizarlo y asegúrese de que se encuentra en un terreno legal sólido, etc.)
Podría usar Reflector para analizar su código fuente y, a partir de esa figura, conocer el funcionamiento interno.
Si quieres evitar la reflexión, puedes usar expresiones. Aquí hay un ejemplo de llamar a un constructor privado con un valor de cadena.
private static Func<string, T> CreateInstanceFunc()
{
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var ctor = typeof(T).GetConstructors(flags).Single(
ctors =>
{
var parameters = ctors.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType == typeof(string);
});
var value = Expression.Parameter(typeof(string), "value");
var body = Expression.New(ctor, value);
var lambda = Expression.Lambda<Func<string, T>>(body, value);
return lambda.Compile();
}
Una alternativa sería nominar a la asamblea convocante como una asamblea "amiga".
Simplemente agregue esto al archivo AssemblyInfo.cs del ensamblado que contiene el constructor interno:
[assembly: InternalsVisibleTo("Calling.Assembly")]
Si no tiene acceso al ensamblado, también puede llamar al constructor directamente (usando Reflection):
MyClass obj = (MyClass) typeof(MyClass).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null).Invoke(null);
internal
no significa que no puedas instanciarlo. Simplemente significa que solo los miembros de la misma asamblea pueden llamarlo.
Para fines de prueba, puede permitir que su conjunto de prueba acceda también a las partes internas utilizando el atributo InternalsVisibleTo. Consulte http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx