2011-12-22 20 views
9

Supongamos que tengo un ensamblado que declara la interfaz interna IInternalInterface. No tengo acceso al código de este ensamblado y no puedo cambiarlo. ¿Cómo puedo crear mi propia implementación de IInternalInterface?Crear tipo que implementa la interfaz interna

Por qué necesito esto: el ensamblado contiene la clase con la lista de implementadores IInternalInterface y mi objetivo es agregar allí mi propia implementación.

Respuesta

4

¿Cómo puedo crear mi propia implementación de IInternalInterface?

Respuesta simple: no se puede. Si los autores del ensamblado decidieron marcar esta interfaz con internal, significa que no querían que el código de otros ensambles utilizara esta interfaz.

+3

oh sí puedes ... usando Reflection.Emit ... pero ten cuidado con los dragones. –

+5

@ noah1989, no puede usar Reflection.Emit para generar una clase que implemente una interfaz interna. Cuando intentes cargar esta clase dinámica, obtendrás una excepción TypeLoadException en tiempo de ejecución. –

+0

Indeed .. "Tipo '...' del ensamblado '...' es intentando implementar una interfaz inaccesible." –

0

Se podría añadir [InternalsVisibleTo()] atributo, pero en lo que no se tiene acceso al código fuente, no se puede implementar esta interfaz en tiempo de compilación

De otro lado, puede hacerlo en tiempo de ejecución. Para esto, debe usar la generación de código de tiempo de ejecución (también conocido como Reflection.Emit) y buscar el tipo de interfaz con BindingFlags.NonPublic. Puede leer más al respecto here.

Actualizado:
como se menciona en los comentarios de abajo, es imposible heredar de una interfaz que no sea pública. Por lo tanto, lamentablemente tiene sin soluciones.

+0

No. Pensé que eso funcionaría, pero [no] (http://stackoverflow.com/questions/4350363/reflectiontypeloadexception-type-is-attempting-to-implement-an-inaccessibleint). –

+0

Gracias. He actualizado mi respuesta. No sabia sobre esto. –

0

También podría usar el redireccionamiento de la versión de ensamblaje o el tipo de redirección para "mover" la declaración de interfaz a un conjunto bajo su control y hacer pública su implementación.

Pero como dijo Darin, asegúrese de pensar dos veces acerca de este enfoque. Puede haber una forma prevista de ampliar la funcionalidad de la biblioteca que sería mucho más limpio ...

+0

¿Qué es la redirección de versión o la redirección de tipo? –

+0

@Orsol: ¡Google es pura Magia! aquí: http://msdn.microsoft.com/en-us/library/7wd6ex19(v=vs.71).aspx y http://msdn.microsoft.com/en-us/library/ms404275.aspx –

+0

@ noah1989 Requiere acceso al código de la asamblea. –

7

Es posible usar el proxy remoto.
Tenga en cuenta que mi respuesta es solo un boceto rápido y podría necesitar una mejora adicional.

internal interface IInternalInterface { 
    void SayHello(); 
} 

// -------------------------------------------------------------- 
// in another assembly 
public class ImplementationProxy : RealProxy, IRemotingTypeInfo { 
    private readonly MethodInfo method; 

    public ImplementationProxy(MethodInfo method) 
     : base(typeof(ContextBoundObject)) 
    { 
     this.method = method; 
    } 

    public override IMessage Invoke(IMessage msg) { 
     if (!(msg is IMethodCallMessage)) 
      throw new NotSupportedException(); 

     var call = (IMethodCallMessage)msg; 
     if (call.MethodBase != this.method) 
      throw new NotSupportedException(); 

     Console.WriteLine("Hi from internals!"); 
     return new ReturnMessage(null, null, 0, call.LogicalCallContext, call); 
    } 

    public bool CanCastTo(Type fromType, object o) 
    { 
     return fromType == method.DeclaringType; 
    } 

    public string TypeName 
    { 
     get { return this.GetType().Name; } 
     set { } 
    } 
}  
1

yo extiendo sobre la respuesta de @AndreyShchekin ya que era muy útil, pero perdió algunos bits:

public class Program 
{ 
    public static void Main() 
    { 
     var internalType = typeof(PublicTypeInAnotherAssembly).Assembly.GetType("Full name of internal type: System.Internals.IInterface"); 

     var result = new InterfaceImplementer(internalType, InterfaceCalled).GetTransparentProxy(); 
    } 

    static object InterfaceCalled(MethodInfo info) 
    { 
     // Implement logic. 
     Console.WriteLine($"{info.Name}: Did someone call an internal method?"); 
     // Return value matching info.ReturnType or null if void. 
     return null; 
    } 
} 

public class InterfaceImplementer : RealProxy, IRemotingTypeInfo 
{ 
    readonly Type _type; 
    readonly Func<MethodInfo, object> _callback; 

    public InterfaceImplementer(Type type, Func<MethodInfo, object> callback) : base(type) 
    { 
     _callback = callback; 
     _type = type; 
    } 

    public override IMessage Invoke(IMessage msg) 
    { 
     var call = msg as IMethodCallMessage; 

     if (call == null) 
      throw new NotSupportedException(); 

     var method = (MethodInfo)call.MethodBase; 

     return new ReturnMessage(_callback(method), null, 0, call.LogicalCallContext, call); 
    } 

    public bool CanCastTo(Type fromType, object o) => fromType == _type; 

    public string TypeName { get; set; } 
} 

result Ahora es asignable a la interfaz interna. Para verificarlo, podemos hacer esto en el conjunto que contiene la interfaz interna:

public class PublicTypeInAnotherAssembly 
{ 
    public void Test(object proxy) 
    { 
     var internalInterface = (IInternalInterface)proxy; 

     internalInterface.MethodOnInterface(); 
    } 
} 

o asociarla con reflexión si no tenemos acceso.

Cuestiones relacionadas