Vreemde inheritance-wijziging

Ik ben een .NET ontwikkelaar en weet vrij veel over OOP. Onlangs zag ik echter een interessant feit.

System.Data.SqlClient.SqlCommand derives from System.Data.Common.DbCommand. The latter implements System.IDbCommand. System.IDbCommand exposes the property Connection which an instance of IDbConnection. In DbCommand However this property returns DbConnection type. And finally the same property in SqlCommand is of type SqlConnection

Ik heb geprobeerd hetzelfde uit te voeren, maar er is een fout opgetreden bij het compileren. Hoe werd dit bereikt in het bovenstaande voorbeeld en hoe kan ik hetzelfde patroon recreëren?

Mijn code (niet compileren):

public interface IFoo { }
public interface IBar 
{
   IFoo TheFoo();
}

public abstract class AbsFoo : IFoo { }
public abstract class AbsBar : IBar 
{
    public abstract AbsFoo TheFoo();
}

public class ConcreteFoo : AbsFoo { }
public class ConcreteBar : AbsBar { }
1
Je moet zowel Jason's en Richard's antwoorden gebruiken om te doen wat SqlCommand doet. Beide antwoorden zijn twee delen van het geheel.
toegevoegd de auteur Enigmativity, de bron

3 antwoord

Explicit interface implementation is the name of the game here. Try this:

public abstract class AbsBar : IBar {
    IFoo IFoo.TheFoo() { return this.TheFoo(); }
    public abstract AbsFoo TheFoo();
}

Hier is een goede handleiding over impliciete versus expliciete implementatie .

4
toegevoegd
Oybek: Juist.
toegevoegd de auteur jason, de bron
Het verbergt zich vanwege de expliciete implementatie van de interface! (Sorry voor de compilatiefout: ik knip en plak voortijdig maar vergeef me alstublieft.) De documentatie is niet verkeerd.
toegevoegd de auteur jason, de bron
@Jason - Je hebt hier maar de helft van de vraag beantwoord. Richard's antwoord was de tweede helft. Het OP moet zowel expliciete interfaceimplementatie als methode-shadowing gebruiken om te bereiken wat SqlCommand doet.
toegevoegd de auteur Enigmativity, de bron
Dit is niet correct en compileert zelfs niet. Impliciete vs. explicte implementatie lost het probleem niet op.
toegevoegd de auteur Richard Hein, de bron
@Jason Welke richtlijnen gebruiken? Je code compileert niet. En de reden waarom SqlCommand Connection-teruggave een ander type kan geven, is dan dat DbCommand is omdat Connection gewoon een openbaar eigendom is. Je compilatiefout is "De modifier 'abstract' is niet geldig voor dit item". De documentatie klopt niet, kijk in Object Browser en je ziet de handtekening voor DbCommand.Connection is openbaar DbConnection Connection {get; vast te stellen; }, en de handtekening voor SqlCommand.Connection is public SqlConnection Connection {get; vast te stellen; }. Ik kijk er recht naar toe.
toegevoegd de auteur Richard Hein, de bron
Als je daarnaast SqlCommand in de Objectbrowser bekijkt en de Inherited Members-show inschakelt, zie je twee Connection-eigenschappen, een die DbConnection retourneert en de andere SqlConnection, maar degene die terugkeert, DbConnection verbergt zich voor de normale toegang.
toegevoegd de auteur Richard Hein, de bron
Ik markeer IBar.TheFoo() als abstract en de tweede methode als openbaar abstract, maar het geeft me nog steeds een compileerfout
toegevoegd de auteur Oybek, de bron
Sorry, ik zie dat het noodzakelijk is om de expliciet gedeclareerde methode te implementeren. Ik kan het niet gewoon abstract maken.
toegevoegd de auteur Oybek, de bron

I have to say that I think Richard was a little hard done by - his answer is just as good as Jason's in that they both only answered half of the question. Put them both together and you have the full answer.

To make this work with IDbCommand, DbCommand & SqlCommand there has to be an explicit implementation of IDbCommand in DbCommand (Jason's answer) and public method shadowing in SqlCommand (Richard's answer).

Ik geef het volledige "Foo/Bar" -voorbeeld.

Begin met deze interfaces:

public interface IFoo
{
    IBar GetBar();
}

public interface IBar { }

Volgende Foo moet een expliciete implementatie van IFoo bevatten om Bar , niet IBar , vanuit zijn eigen < code> GetBar methode:

public abstract class Foo : IFoo
{
    IBar IFoo.GetBar()
    {
        return this.GetBar();
    }

    public Bar GetBar()
    {
        return this.GetBarInner();
    }

    protected abstract Bar GetBarInner();
}

public abstract class Bar : IBar { }

En ten slotte moet een SomeFoo -klasse GetBar overschaduwen om een ​​ SomeFoo -instantie te kunnen retourneren:

public class SomeFoo : Foo
{
    public new SomeBar GetBar()
    {
        return new SomeBar();
    }

    protected override Bar GetBarInner()
    {
        return this.GetBar();
    }
}

public class SomeBar : Bar { }

Ik denk dat de enige informatie die Richard is dat mijn toevoeging van het nieuwe sleutelwoord aan de overschaduwde methode je de compileerfout doet verdwijnen.

2
toegevoegd
Hoera voor observaties van derden. :)
toegevoegd de auteur Richard Hein, de bron

Verbinding in DbCommand en SqlCommand zijn beide slechts openbare methoden. Er zou een compileerwaarschuwing zijn, maar het is toegestaan. Uw code zou meer als volgt moeten zijn om te werken zoals SqlCommand/DbCommand:

    public interface IFoo { }
public abstract class AbsBaseBar
{
    public IFoo TheFoo() { throw new NotImplementedException(); }
}
public class AbsFoo : IFoo { }
public class AbsBar : AbsBaseBar
{
    public AbsFoo TheFoo() { throw new NotImplementedException(); }
}

public class ConcreteFoo : AbsFoo { }
public class ConcreteBar : AbsBar { } 
1
toegevoegd
Ik denk dat je het een beetje moeilijk hebt gedaan door hier Richard - je hebt wel de helft van de vraag heel goed beantwoord. En Jason heeft je helft helemaal gemist. Ik gaf je een upvote om van het negatieve af te komen.
toegevoegd de auteur Enigmativity, de bron