2009-01-03 19 views
9

he implementado recientemente una clase como:¿Por qué implementación de interfaz explícita?

class TestClass : IDisposable 
{ 
    RegistryKey m_key; 
    public TestClass() 
    { 
     m_key = Registry.CurrentUser.OpenSubKey("Software", false); 
    } 

    public void Dispose() 
    { 
     // m_key.Dispose(); 
     IDisposable disp = m_key; 
     disp.Dispose(); 
    } 
} 

Si descomentar la llamada directa a disponer, consigo CS0117 de error (" 'Microsoft.Win32.RegistryKey' no contiene una definición para 'botar'"). Algunos de Google me llevaron al this thread, donde aprendí lo que estaba pasando, así que ahora entiendo su mecánica. La documentación de MSDN sugiere que el autor preferiría que llame a Close() en lugar de Dispose(), pero no explica por qué.

¿Cuál es el propósito de este patrón (que creo que también lo he visto en las clases IO)? A la luz del hecho de que esta fue una decisión intencional del autor de la clase, ¿qué tan malo es el código anterior (la llamada a Dispose a través de la interfaz IDisposable)? No puede ser tan malo, después de todo, es lo que sucedería en una declaración de uso, ¿verdad?

[ediciones: 1) cambiar el título de "no público" a "explícito" 2) eliminan la implementación explícita de mi código, dejado accidentalmente en la experimentación]

+1

Puede usar '(m_key como IDisposable) .Disponer();' para abreviar. –

Respuesta

15

Esto se llama aplicación interfaz explícita. En su ejemplo, ya que define el método Dispose() como "void IDisposable.Dispose()" también está implementando explícitamente la interfaz IDisposable.

Esto normalmente se hace para evitar colisiones. Si Microsoft alguna vez hubiera querido agregar otro método Dispose() que hiciera algo distinto a RegistryKey, no lo haría a menos que utilizara la implementación explícita de esa interfaz.

Esto se hace a menudo con la interfaz genérica IEnumerable < T >. También requiere que implemente la interfaz no genérica IEnumerable. El único miembro en estas dos interfaces es GetEnumerator, con el genérico de ser más útil, por lo que su implementa normalmente como esto:

public clas SomeClass : IEnumerable<SomeOtherClass> 
{ 
    public IEnumerator<SomeOtherClass> GetEnumerator() 
    { 
     ... 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

esta manera, cuando se llama a un objeto de método GetEnumator de SomeClass, llama a la versión genérica, ya que el otro se implementó de manera explícita, lo que nos permite obtener los genéricos de fuerte tipado.

Ver páginas 166-169 de Programación C# de Jesse Liberty (Tengo la cuarta edición).

-2

mayoría de las personas no están de acuerdo conmigo, pero me gusta usar implementación de interfaz explícita para todos interfaces de. Quiero dejar en claro si estoy escribiendo un método para llamar a mi objeto o en mi interfaz.

Esto es doloroso si tiene una referencia al objeto y desea llamar a un método de interfaz (como el ejemplo anterior), pero mitigarlo mediante la escritura:

class C : IDisposable 
{ 
    public IDisposable IDisposable { get { return this; } } 
    void IDisposable.Dispose() { } 
} 

lo que significa que una llamada al método de C parece:

C c = ... 
c.IDisposable.Dispose(); 

el compilador analiza esto como "llame a la propiedad IDisposable en C, a continuación, llamar al método Dispose() en el resultado", pero lo leí como "llame al método IDisposable.Dispose() en C" w que parece natural aquí.

Este enfoque puede ponerse desagradable cuando se utilizan interfaces genéricas, desafortunadamente.

+0

Lo mismo aquí en las interfaces explícitas. Los genéricos lo hacen muy difícil a veces. – JaredPar

+32

Eek: la implementación explícita de la interfaz no solo hace más difícil para la persona que llama sin soluciones como la suya anterior, sino que también arruina la herencia: se llega a situaciones difíciles si una clase derivada desea anular un método de interfaz. Lo evito cuando puedo. –

+0

Las clases heredables que implementan interfaces deberían hacerlo con miembros públicos virtuales (en los casos en que los miembros deberían ser accesibles como miembros de la clase) o con miembros protegidos virtuales cuyo nombre es una forma modificada del nombre o firma del miembro de la interfaz. VB.NET permite que esto último se haga directamente 'Protegido Anulable Sub Foo_Impl() Implementa IInterface.Foo' pero en C# lo mejor que se puede hacer es definir un método virtual protegido para contener las" agallas "de la interfaz, y tener la interfaz la implementación no hace más que encadenar a eso. – supercat

Cuestiones relacionadas