Laden...

Fluent Interface für Klassen für HTML-Tags

Erstellt von Curse4Life vor 12 Jahren Letzter Beitrag vor 12 Jahren 1.446 Views
C
Curse4Life Themenstarter:in
452 Beiträge seit 2005
vor 12 Jahren
Fluent Interface für Klassen für HTML-Tags

Kaum ist das eine Problem gelöst oder besser umgangen, tritt auch schon das nächste auf.
Ich versuche das Problem wieder möglichst kurz zu erklären.

Ich versuche ein Fluent Interface für Html Tags zu basteln.

Mein Grundgedanke und erster Versuch war in etwa dieser:


public interface IHtmlTag
{
  IHtmlTag Id(String);


  IHtmlTag Class(String);
}


public class HtmlTag : IHtmlTag
{
  public IHtmlTag Id(String)
  {
    ...
    return this;
  }


  public IHtmlTag Class(String)
  {
    ...
    return this;
  }
}


public class aTag : HtmlTag
{
  public IHtmlTag Href(String)
  {
    ...
    return this;
  }
}


public class divTag : HtmlTag
{
  public IHtmlTag Float(String)
  {
    ...
    return this;
  }
}

So, das hat soweit auch ganz gut funktioniert, bis mir auffiel, das ich bei einem Link dann die aTag Funktionen verliere sobald ich eine HtmlTag Funktion wie z.B. "Id" aufrufe, da ich ja ein IHtmlTag zurückgebe, was zur Laufzeit natürlich ein aTag ist, aber das versteht er so nicht.

Gut dachte ich, versuche ich es etwas anders:


public interface IHtmlTag<TModel>
{
  TModel Id(String);


  TModel Class(String);
}


public class HtmlTag<TModel> : IHtmlTag<TModel>
{
  public TModel Id(String)
  {
    ...
    return this;
  }


  public TModel Class(String)
  {
    ...
    return this;
  }
}


public class aTag : HtmlTag<aTag>
{
  public aTag Href(String)
  {
    ...
    return this;
  }
}


public class divTag : HtmlTag<divTag>
{
  public divTag Float(String)
  {
    ...
    return this;
  }
}

ich dachte das wäre schon ganz schön klever, aber das denkt wohl der Compiler nicht, weil das kann ich nicht kompelieren, die Fehlermeldung ist das sich "this" nicht in TModel konvertieren lässt obwohl das zur Laufzeit genau das wäre, aber eben nicht zur Compilerzeit. 😭

Andere Ideen wie ich mein Vorhaben umsetzen kann?

Liebe Grüße

PS: Am Ende soll es halt so aussehen:


var blub = new aTag().Id("").Href("");

C
1.214 Beiträge seit 2006
vor 12 Jahren

Das einzige was ich mir auf die Schnelle vorstellen könnte, wäre den Id und Class Methoden Typparameter mitzugeben, etwa so:


public interface IHtmlTag
{
  T Id<T>() where T: IHtmlTag;
}

Und dann würde der Code so ausschauen:


var blub = new aTag().Id<aTag>("").Href<aTag>("");

C
Curse4Life Themenstarter:in
452 Beiträge seit 2005
vor 12 Jahren

Danke dir, aber diese Lösung ist leider für meinen Fall nicht praktikabel, das ist zu viel Tiparbeit und wird dann auch zu unübersichtlich, aber trotzdem danke!

C
1.214 Beiträge seit 2006
vor 12 Jahren

Dann kannst du noch die Id Methode jeweils ausblenden, etwa so:


class aTag: HtmlTag
{
  public new aTag Id()
  {
    return base.Id<aTag>();
  }
}

Dann müsste es auch gehen, wobei du natürlich in jeder abgeleiteten Klasse die Methode neu definieren müsstest...
Wegen der Syntax bin ich mir nicht sicher, hab das noch nie verwendet 😃

Bzw., vielleicht würde auch folgendes gehen:


interface IHtmlTag<T> where T: IHtmlTag
{
  T Id();
}

class HtmlTag<T>: IHtmlTag<T>
{
  public T Id()
  {
     return this as T;
  }
}

class aTag: HtmlTag<aTag>
{
...
}

C
Curse4Life Themenstarter:in
452 Beiträge seit 2005
vor 12 Jahren

Ich werde nicht mehr, das funktioniert!!!
Danke!!

Aber was ist denn der Unterschied zwischen:


return (TModel)this; // Was ich versucht habe, und
return this as TModel;

???

C
1.214 Beiträge seit 2006
vor 12 Jahren

Ähm, jetzt hab ich beim Schreiben völlig vergessen, dass du das auch schon probiert hattest 😄
Naja, ich würde sagen, der Unteschied ist der Constraint.

C
Curse4Life Themenstarter:in
452 Beiträge seit 2005
vor 12 Jahren

Das sagt mir leider nichts, ich benutze dieses "as" nicht, ich weiß nur das es das gibt 😃

S
417 Beiträge seit 2008
vor 12 Jahren

Hallo,

wie sieht denn die funktionsfähige Lösung jetzt aus?

C
Curse4Life Themenstarter:in
452 Beiträge seit 2005
vor 12 Jahren
return this as TModel;

Das funktioniert.

Mein versuch this zu casten mit (TModel)this hat nicht funktioniert.

1.002 Beiträge seit 2007
vor 12 Jahren

Hallo Curse4Life,

siehe [Tipp] Casten aber richtig: Begriffe wie Cast / is / as. Kurz:*Der harte Cast per (TModel)this löst eine InvalidCastException aus, wenn der Cast fehlschlägt. *this as TModel versucht, this in den Datentyp TModel zu casten. Gelingt das nicht, ist das Resultat null; es wird keine Exception ausgelöst.

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

C
Curse4Life Themenstarter:in
452 Beiträge seit 2005
vor 12 Jahren

Dankeschön, werde ich mir mal zu Gemüte führen.

Gute Nacht an alle

S
417 Beiträge seit 2008
vor 12 Jahren

Ich meinte eigentlich den gesamten Source-code. Ganz verstehe ich noch nicht wie die folgende Lösung funktionieren soll:

interface IHtmlTag<T> where T: IHtmlTag
{
  T Id();
}

class HtmlTag<T>: IHtmlTag<T>
{
  public T Id()
  {
     return this as T;
  }
}

HtmlTag<T> und die abgeleiteten Klassen implementieren doch IHtmlTag (nicht das generische Interface) nicht. wie kann da der Cast später gut gehen? Für was ist eigentlich das nicht-generische Interface?
Oder überseh ich grad was?

C
1.214 Beiträge seit 2006
vor 12 Jahren

Oder überseh ich grad was?

Ja, du kennst die C# Syntax anscheinend nicht gut genug 😉 Das ist keine Ableitung, das ist ein Type Constraint.

S
417 Beiträge seit 2008
vor 12 Jahren

das war schon klar 😉
Das Constraint sagt ja, dass T das IHtmlTag implementieren muss.
Später wird bei public T Id() dann this as T zurückgegeben, obwohl "this", also weder IHtmlTag<T>, noch HtmlTag<T> das IHtmlTag interface implementieren.
Und nun?

C
1.214 Beiträge seit 2006
vor 12 Jahren

Es gibt kein nicht generisches Interface IHtmlTag. Das bedeutet nur, dass der Typparameter wieder etwas sein muss, dass von IHtmlTag<T> abgeleitet ist. Das verhindert, dass man sowas wie aTag: HtmlTag<int> definiert. Das ist eine rekursive Definition, weiß jetzt nicht, obs dafür einen speziellen Namen gibt.