[c#] Overerving gaat mis of verkeerd toegepast?

Pagina: 1
Acties:
  • 155 views sinds 30-01-2008
  • Reageer

  • reyan
  • Registratie: November 2003
  • Laatst online: 17-12-2021
Ik heb een hele reeks controls die erven van een user control. Deze geërfde controls hebben natuurlijk een groot aantal gemeenschappelijke velden, maar ook een aantal verschillende. Een simplistische weergave:

Base:

[MyUserControl]
• Size (Size)
• Required (bool)
• Text (string)
• Value (string)

Volgende controls erven van bovenstaande base-control:

[MyUserControl_Integer]
• Size (Size)
• Required (bool)
• Text (string)
• Value (int)

[MyUserControl_TimeSpan]
• Size (Size)
• Required (bool)
• Text (string)
• Value (TimeSpan)

[MyUserControl_String]
• Size (Size)
• Required (bool)
• Text (string)
• Value (string)
• MaxLength (int)

Zoals je kunt zien erven ze properties van de base-control, maar er zijn ook properties met een verschillend type maar dezelfde naam (Value).

Dat is allemaal geen probleem, maar nu wil ik een nieuwe user control maken waarin bovenstaande controls gebruikt worden.

Base:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SpecialUserControl : UserControl
{
    protected MyUserControl myUserControl = new MyUserControl

    [.. code .. properties ..]

    [Category("Value"), DefaultValue(""), Browsable(true), Bindable(true)]
    public string Value
    {
        get { return this.myUserControl.Value; }
        set { this.myUserControl.Value = value; }
    }
}
Ook de andere geërfde MyUserControls wil ik in een gelijkaardige SpecialUserControl opnemen. Aangezien de MyUserControls allemaal van dezelfde basis afgeleid zijn, het merendeel van de properties gelijk is, leek het me nogal stom om telkens een nieuwe SpecialUserControl aan te maken. Het lijkt me interessanter om ook deze gewoon te laten erven van SpecialUserControl:

Erft van bovenstaande base-control:
C#:
1
2
3
4
5
6
7
8
9
10
11
public class SpecialUserControl_Integer : SpecialUserControl
{
    protected new MyUserControl_Integer myUserControl = new MyUserControl_Integer

    [Category("Value"), DefaultValue(0), Browsable(true), Bindable(true)]
    public new int Value
    {
        get { return this.myUserControl.Value; }
        set { this.myUserControl.Value = value; }
    }
}
Dat geeft natuurlijk een aardige reductie in de te schrijven code. Door het keywoord 'new' overschrijf ik MyUserControl met MyUserControl_Integer.

Maar... Het werkt niet. Alle properties uit de SpecialUserControl base class hebben totaal geen invloed op de MyUserControl_Integer control en andersom geldt hetzelfde. Het lijkt er dus op dat ik met het new keyword niet MyUserControl heb vervangen door MyUserControl_Integer, maar gewoon verborgen heb voor de afgeleide class.

Dit is een in mijn geval onbruikbaar gedrag natuurlijk. Ik wil dat de MyUserControl uit de base class vervangen wordt door de MyUserControl_Integer. Het override keyword is echter niet beschikbaar bij deze declaratie.

Hoe doe je dit wel goed?

Verwijderd

Maak je baseclass functies en properties virtual. Dat zal waarschijnlijk je probleem oplossen. :)

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Of doe het anders, laat de properties die je in alle subklassen gebruikt in de base staan en definieer Value apart in elke subklasse.

Nu met Land Rover Series 3 en Defender 90


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 14-02 12:54
Of comineer beide: zet de gemeenschappelijke functies in de base-class en definieer degene die de subclasses moeten implementeren daar virtual/asbtract :P

edit:
Weet ff niet welke van toepassing is, maar daar kom je zelf zo achter.

[ Voor 25% gewijzigd door riezebosch op 12-06-2006 15:24 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • reyan
  • Registratie: November 2003
  • Laatst online: 17-12-2021
Jullie kijken teveel naar de properties enzeo denk ik. Daar zit niet het probleem.

Het probleem ontstaat doordat de base een andere control gebruikt dan z'n afgeleiden (alhoewel die op hun beurt wel afgeleiden zijn). Let op het verschil in declaratie van myUserControl in de base class en de afgeleide:
C#:
1
2
3
4
5
protected MyUserControl myUserControl = new MyUserControl();

VS

protected new MyUserControl_Integer myUserControl = new MyUserControl_Integer();
Dat valt niet op te lossen door de functies enzo virtual te maken. Ik zou die myUserControl virtual moeten kunnen maken, maar dat gaat natuurlijk niet.

Wanneer ik het new keyword gebruik (zoals in het voorbeeld), dan wordt (MyUserControl)myUserControl verborgen in de afgeleide class, maar alle properties uit de base class blijven op (MyUserControl)myUserControl werken in plaats van zoals ik wil op de nieuwe (new) (MyUserControl_Integer)myUserControl.

  • reyan
  • Registratie: November 2003
  • Laatst online: 17-12-2021
Laat ik anders een eenvoudiger voorbeeld geven:
C#:
1
2
3
4
5
6
7
8
9
public class Basis
{
    protected int variabele = 5;

    public void Verdubbel()
    {
        this.variabele = this.variabele * 2;
    }
}
Stel nu dat ik hier een afgeleide class van wil maken waarbij variabele geen integer is, maar een double. Ik zou bovenstaande class kunnen kopiëren en int door double vervangen, maar stel dat de class uitgebreider is dan wordt dat onzinnig: Nutteloze dubbele code.
De functies in de class zijn nochtans type-onafhankelijk. Het maakt voor Verdubbel() niet uit of variabele een integer of een double is.

Maar een constructie als volgt werkt niet:
C#:
1
2
3
4
public class Afgeleide : Basis
{
    protected new double variabele = 13.4;
}
Verdubbel() blijft werken op de integer uit de base class...

  • whoami
  • Registratie: December 2000
  • Laatst online: 20-02 21:53
De eerste vraag die je je moet stellen is, is m'n afgeleide control wel een base control ?
Of erf je gewoon omdat je een aantal gemeenschappelijke 'velden' hebt ?
Als je wil weten of het een goed idee is om een specifieke control van 'MyBaseControl' te inheriten, dan moet je je de vraag stellen of je kan programmeren tegen de base class.


Zijn de properties die je wil 'veranderen' in je afgeleide class, in je base class als virtual gedeclareerd ?

Wat je kan doen, is je Value als object definieren, en dan at runtime checken in iedere specifieke UserControl of er wel een variable van het goede type aan toegekend wordt.

https://fgheysels.github.io/


  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!


Gebruik anders generics:
[code=c#]
public class Base<T> {
private <T> variabele;

public void Verdubbel()
{
this.variabele = this.variabele * 2;
}
}
[/code]
je kunt dan vervolgens dit doen:
[code=c#]
Base<int> bInt = new Base<int>();
Base<double> bDouble = new Base<double>();

bInt.Verdubbel();
bDouble.Verdubbel();
[/code]

Nadeel is wel dat je T een overload voor * moet hebben.


Ik praat poep. Dit werkt niet omdat T niets weet van Add of + of * |:(

[ Voor 16% gewijzigd door MTWZZ op 12-06-2006 16:05 ]

Nu met Land Rover Series 3 en Defender 90


  • Mastermind
  • Registratie: Februari 2000
  • Laatst online: 17-01 10:57
C#:
1
2
3
4
5
public override Object Value 
    { 
        get { return this.myUserControl.Value; } 
        set { this.myUserControl.Value = value; } 
    } 


Dan kun je 'm casten.

  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 03-02 20:14
reyan schreef op maandag 12 juni 2006 @ 15:47:
onafhankelijk. Het maakt voor Verdubbel() niet uit of variabele een integer of een double is.

Maar een constructie als volgt werkt niet:
C#:
1
2
3
4
public class Afgeleide : Basis
{
    protected new double variabele = 13.4;
}
Verdubbel() blijft werken op de integer uit de base class...
Ik ken niet veel C# maar in java zou je toch de variabele van je afgeleide klasse moeten uitlezen, de variabele met zelfde naam (mag zelf ander type zijn) uit je basis klasse lees je uit via super .

maar als ik zo de constructie bekijk die je aan het maken ben betwijfel ik toch of overervering de beste oplossing is, het wordt er enkel maar ingewikkelder op...

"Live as if you were to die tomorrow. Learn as if you were to live forever"


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 03:42
Volgens mij is de kern van het probleem dat reyan denkt variabelen te kunnen 'vervangen' in derived classes, met gebruik van het 'new' keyword. Dat is dus niet zo; met het 'new' keyword verberg je wel de member voor de derived class, maar niet voor de base class. Het enige wat 'new' je geeft, is dat je een identifier kunt hergebruiken.

Om het verdubbel-voorbeeld te hanteren: class Afgeleide heeft gewoon twee member variables: een 'int variabele' en een 'double variabele'; de eerste wordt gewoon nog gebruikt door Verdubbel. Omdat ze dezelfde naam hebben, is het dus afhankelijk van de statische (compile time) scope naar welke verwezen wordt als je aan 'variabele' refereert.

C# is qua typering gewoon niet erg flexibel (en terecht: dat is het hele idee van type safe programming). Je kunt in een afgeleide klasse wel de waarden van variabelen wijzigen, en zelfs de implementatie van methoden, maar niet de typen van gedeclareerde members. Om een lang verhaal kort te maken: je zult een andere implementatie moeten bedenken. Generics lijkt me hier niet van toepassing, maar je zou je waarde gewoon als 'object' kunnen declareren; dan kan elke klasse er een ander object aan toekennen. Dat gaat wel tenkoste van wat typesafety, maar dat is onvermijdelijk.

[ Voor 19% gewijzigd door Soultaker op 12-06-2006 16:22 ]


  • reyan
  • Registratie: November 2003
  • Laatst online: 17-12-2021
Dat new niet doet wat ik hoopte wist ik ondertussen al, maar ik hoopte dat er een andere eenvoudige manier was. Ik gebruikte new eerder als voorbeeld.

Hoe dan ook, aan jullie commentaren te zien is dit niet echt een goede werkwijze. De hoofdreden dat ik wou erven was om dubbele code te besparen, maar in dit geval is de base class eigenlijk niet echt een goede base class. Ik ga het geheel herschrijven zodat ik wel een zuivere base class krijg waarvan ik makkelijk kan erven.

Bedankt voor jullie inzichten!

  • Mastermind
  • Registratie: Februari 2000
  • Laatst online: 17-01 10:57
Je kunt toch gewoon een Object gebruiken als Value, en die in de andere classes casten, of zie ik het nou verkeerd :?

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 00:09

Janoz

Moderator Devschuur®

!litemod

Dat is eigenlijk ook het eerste waar ik aan dacht. Hoe wil je anders je 'contract' rond krijgen? Het hele idee van overerven is dat je op een bepaalde plek gewoon gebruik kunt maken van de base class zonder dat je exact weet van welke subclass die instantie is.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'

Pagina: 1