Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Rückgabetyp = Typ der aktuellen Klasse: ohne Generics?
Gandi
myCSharp.de - Member



Dabei seit:
Beiträge: 7
Herkunft: Bayern

Themenstarter:

Rückgabetyp = Typ der aktuellen Klasse: ohne Generics?

beantworten | zitieren | melden

Ich weiß, bescheuerter Threadname.

Jedenfalls hätte ich mal eine kurze Frage: Gibt es einen eleganteren Weg, um das hier zu erreichen?


public class Base
{
    public Base()
    {
    }

    public static virtual T Create<T>() where T : Base, new()
    {
        if (SomeCondition())
            return new T();
        
        return null;
    }
}

public class A : Base
{ 
}

public class B : Base
{
}

public class C : Base
{
}

public class Program
{
    public static void Main()
    {
        A x = A.Create<A>(); // <- Hier möchte ich das <A> weglassen können, es soll immer das selbe tun nur halt ein A returnen
        B x2 = B.Create<B>(); // <- Hier möchte ich das <B> weglassen können, es soll immer das selbe tun nur halt ein B returnen
    }
}

Ich könnte die Create-Methode natürlich in jeder Klasse überschreiben, aber das wäre ja auch unschön.

Gibt es also einen besseren Weg, so etwas zu erreichen?
private Nachricht | Beiträge des Benutzers
JuyJuka
myCSharp.de - Experte

Avatar #avatar-2316.jpg


Dabei seit:
Beiträge: 2.187
Herkunft: Deutschland

beantworten | zitieren | melden

Hallo Gandi,

Warum machst du das mit einer statischen Mehtode?
Du solltest dir hierfür lieber das Factory-Pattern (oder AbstractFactory-Pattern) ansehen.

Gruß
Juy Juka
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.779
Herkunft: Waidring

beantworten | zitieren | melden

Hallo,


public static virtual T Create<T>()
Lässt sich schon mal gar nicht kompilieren. Entweder static oder virtual.

Was stört dich denn am generischen Typen? Ich finde das so eh elegant ;-)


mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
Gandi
myCSharp.de - Member



Dabei seit:
Beiträge: 7
Herkunft: Bayern

Themenstarter:

beantworten | zitieren | melden

Wow, das geht in diesem Forum aber verdammt schnell mit Antworten
Zitat
Lässt sich schon mal gar nicht kompilieren. Entweder static oder virtual.

Ups, das kommt davon wenn man sich in ner Minute schnell einen Beispielcode zusammenbastelt^^ Das "virtual" gehört da natürlich nicht hin.
Zitat
Was stört dich denn am generischen Typen? Ich finde das so eh elegant ;-)

Ja, ist schon gar nicht so übel (ich finde es zumindest schöner als das Factory-Pattern das JuyJuka vorgeschlagen hat), aber ich hatte gehofft dass mich C# auch hier nicht enttäuschen würde und man das noch schöner lösen kann
private Nachricht | Beiträge des Benutzers
xxxprod
myCSharp.de - Experte

Avatar #avatar-2329.gif


Dabei seit:
Beiträge: 1.378
Herkunft: Österreich\Wien

beantworten | zitieren | melden

Nicht schöner aber anders...


public class A : Base
{
   public static A Create(){  }
}

public class B : Base
{
   public static B Create(){  }
}

public class C : Base
{
   public static C Create(){  }
}

public class Program
{
    public static void Main()
    {
        A x = A.Create();
        B x2 = B.Create();
    }
}

Lg XXX
private Nachricht | Beiträge des Benutzers
MarsStein
myCSharp.de - Experte

Avatar #avatar-3191.gif


Dabei seit:
Beiträge: 3.163
Herkunft: Trier -> München

beantworten | zitieren | melden

Hallo,

diese Variante ist zwar auch generisch, hat aber den Vorteil, daß das Typargument nur in der Klassendeklaration, nicht aber im Aufruf verwendet werden muss:

public class Base<T> where T : Base<T>, new()
{
    public Base()
    {
    }

    public static T Create()
    {
        if (SomeCondition())
            return new T();

        return null;
    }
}

public class A : Base<A> // <- <A> nur bei Klassendeklaration nötig
{
}

public class B : Base<B> // <- <B> nur bei Klassendeklaration nötig
{
}

public class C : Base<C>
{
}

public class Program
{
    public static void Main()
    {
        A x = A.Create(); // <- kein <A> mehr nötig
        B x2 = B.Create(); // <- kein <B> mehr nötig
    }
}

Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
private Nachricht | Beiträge des Benutzers
N1ls
myCSharp.de - Member



Dabei seit:
Beiträge: 46

beantworten | zitieren | melden

Hallo,

gibt es für soetwas irgendeinen praktischen Nutzen? Tschuldigung, dass ich nachfrage. Aber für mich sieht das irgendwie so aus, als ob Du C# eine Delphi-Syntax aufzwingen willst...

Grüsse,

N1ls
private Nachricht | Beiträge des Benutzers
Gandi
myCSharp.de - Member



Dabei seit:
Beiträge: 7
Herkunft: Bayern

Themenstarter:

beantworten | zitieren | melden

@MarsStein: Das sieht schonmal gar nicht schlecht aus, ich wusste gar nicht dass man die Klasse selbst in den Generic-Parametern benutzen kann... wieder was gelernt :)

@N1ls: Was genau meinst du? Das mit dem statischen "Create" oder dass die Syntax dafür möglichst kurz sein soll? Für das "Create" habe ich jedenfalls einen praktischen Nutzen, ist allerdings etwas aufwändiger zum erklären. Und Delphi kann ich auf jeden Fall mal nicht^^
private Nachricht | Beiträge des Benutzers
rasepretrep
myCSharp.de - Member



Dabei seit:
Beiträge: 103

beantworten | zitieren | melden

Hmm ganz nett, aber auch beim letzten Ansatz von MarsStein hast Du das Problem dass du einen public Konstruktor in den Klasssen A,B ,C haben musst.
Deshalb hält es keinen unwissenden Programmierer davon ab einfach

a x = new A();

zu schreiben und das wars dann mit der netten Create() Methode.
Ich würde hier eher zu Interfaces tendieren und wenn Du schon eine extra Create Methode hast die Konstruktoren auf protected aetzen, damit keiner new() verwenden kann.
private Nachricht | Beiträge des Benutzers
MarsStein
myCSharp.de - Experte

Avatar #avatar-3191.gif


Dabei seit:
Beiträge: 3.163
Herkunft: Trier -> München

beantworten | zitieren | melden

Hallo,
Zitat
Hmm ganz nett, aber auch beim letzten Ansatz von MarsStein hast Du das Problem dass du einen public Konstruktor in den Klasssen A,B ,C haben musst.
Richtig. Aufgrund von where T: new() muss ein öffentlicher, parameterloser Konstruktor vorliegen.
Das ist bei der statischen Methode im Startbeitrag allerdings auch nicht anders, weshalb ich davon ausging, daß das kein Problem darstellt. Man bekommt halt keinen Zugriffsschutz, kann aber trotzdem mit der Create-Methode die Objekte in Abhängigkeit von internen Faktoren erstellen.

Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
private Nachricht | Beiträge des Benutzers
rasepretrep
myCSharp.de - Member



Dabei seit:
Beiträge: 103

beantworten | zitieren | melden

Hmm, habe mal das Beispiel von MarsStein etwas umgebaut. So würde es auch gehen


public class Base<T> where T : Base<T>
    {
        protected Base()
        {
        }

        public static T Create()
        {
            if (SomeCondition())
                return (T) Activator.CreateInstance(typeof(T),true);
            return null;
        }
    }

    public class A : Base<A> // <- <A> nur bei Klassendeklaration nötig
    {
        protected A()
        {
        }
    }


So kann man kein

A = new A() 

schhreiben, sondern muss auf A.Create() ausweichen...
private Nachricht | Beiträge des Benutzers
MarsStein
myCSharp.de - Experte

Avatar #avatar-3191.gif


Dabei seit:
Beiträge: 3.163
Herkunft: Trier -> München

beantworten | zitieren | melden

Hallo rasepretrep,

ist zwar etwas besser, verschiebt aber nur das Problem:
So ist keiner mehr gezwungen, einen parameterlosen Konstruktor für erbende Klassen zu erstellen. Wenn's keinen gibt, kommt die Exception dann erst zur Laufzeit.


// so gibt's den Kracher:
    public class A : Base<A> // <- <A> nur bei Klassendeklaration nötig
    {
        protected A(string initStr)
        {
        }
    }
Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
private Nachricht | Beiträge des Benutzers
rasepretrep
myCSharp.de - Member



Dabei seit:
Beiträge: 103

beantworten | zitieren | melden

Stimmt, hast recht. Schade dass man nicht sowas wie

public class Base<T> where T : Base<T>, protected new()

schreiben kann ;)
private Nachricht | Beiträge des Benutzers
spike24
myCSharp.de - Member



Dabei seit:
Beiträge: 443
Herkunft: Steiermark Österreich

beantworten | zitieren | melden

Schuss in den Nebel:

public abstract class Base<T> where T : Base<T>
{
    public Base()
    {
    }

    public static T Create()
    {
        if (SomeCondition())
            return CreateInstance();

        return null;
    }

    protected abstract T CreateInstance();

}

public class A : Base<A> // <- <A> nur bei Klassendeklaration nötig
{
  protected override A CreateInstance()
  {
    return new A();
  }
}

public class B : Base<B> // <- <B> nur bei Klassendeklaration nötig
{
  protected override B CreateInstance()
  {
    return new B();
  }
}

public class C : Base<C>
{
  protected override B CreateInstance()
  {
    return new B();
  }
}

public class Program
{
    public static void Main()
    {
        A x = A.Create(); // <- kein <A> mehr nötig
        B x2 = B.Create(); // <- kein <B> mehr nötig
    }
} 

// EDIT Copyright MarsStein
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von spike24 am .
mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen
private Nachricht | Beiträge des Benutzers
MarsStein
myCSharp.de - Experte

Avatar #avatar-3191.gif


Dabei seit:
Beiträge: 3.163
Herkunft: Trier -> München

beantworten | zitieren | melden

Hallo spike24,

das geht so leider gar nicht.
Du kannst CreateInstance nicht in Create aufrufen, weil sie nicht static ist.
Du bräuchtest also zuerst eine Instanz, um die Methode aufzurufen - wsa in dem Zusammenhang natürlich unsinnig ist.

    public static T Create()
    {
        if (SomeCondition())
            return CreateInstance(); // <-- hier bräuchtest Du eine Instanz, um die Methode aufzurufen.

        return null;
    }


Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
private Nachricht | Beiträge des Benutzers
spike24
myCSharp.de - Member



Dabei seit:
Beiträge: 443
Herkunft: Steiermark Österreich

beantworten | zitieren | melden

oh, static, übelesen

    public interface ICreateable<T> where T : Base<T>
    {
      T CreateInstance();
    }

    public abstract class Base<T> where T : Base<T>
    {

      private static Dictionary<Type, object> mFactories;

      static Base()
      {
        mFactories = new Dictionary<Type, object>();
        mFactories.Add(typeof(A), new A());
        mFactories.Add(typeof(B), new B());
        mFactories.Add(typeof(C), new C());
      }

      public Base()
      {
      }

      public static T Create()
      {
        if (SomeCondition())
        {
          ICreateable<T> factory = (ICreateable<T>)mFactories[typeof(T)];
          return factory.CreateInstance();
        }

        return null;
      }

    }

    public class A : Base<A>, ICreateable<A> // <- <A> nur bei Klassendeklaration nötig
    {
      public A CreateInstance()
      {
        return new A();
      }
    }

    public class B : Base<B>, ICreateable<B> // <- <B> nur bei Klassendeklaration nötig
    {
      public B CreateInstance()
      {
        return new B();
      }
    }

    public class C : Base<C>, ICreateable<C>
    {
      public C CreateInstance()
      {
        return new C();
      }
    }

aber von schön sind wir nun auch schon ein Stück weit entfernt.

//Edit: man sollte builden bevor man postet
// Edit 2: Ups, sorry!
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von spike24 am .
mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen
private Nachricht | Beiträge des Benutzers
MarsStein
myCSharp.de - Experte

Avatar #avatar-3191.gif


Dabei seit:
Beiträge: 3.163
Herkunft: Trier -> München

beantworten | zitieren | melden

Hallo,

probier doch Deinen Code bitte mal aus bevor Du ihn postest. Soll ich das jetzt etwa korrigieren?
Und was willst Du mit dem Finalizer??

~Base()

Gruß, MarsStein

EDIT: OK danke für die Korrektur. Das Unschöne an der Lösung besteht darin, daß Deine Basisklasse im statischen Konstruktor alle Kindklassen kennen muss. Aber bevor Du jetzt anfängst, eine statische Methode zu basteln, mit der sich die Kindklassen registrieren sollen - gefragt war nach etwas eleganterem als der Code im Startbeitrag.

Außerdem sind jetzt auch die Konstruktoren wieder public, damit besteht kein Vorteil mehr zu meinem ursprünglichen Vorschlag - wobei sie bei Dir internal sein könnten..

Zusätzlich ist die CreateInstance-Methode public und muss es wegen dem Interface auch bleiben - mehr Schutz gibt's da also auch nicht.
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
private Nachricht | Beiträge des Benutzers
N1ls
myCSharp.de - Member



Dabei seit:
Beiträge: 46

beantworten | zitieren | melden

Zitat von Gandi
@N1ls: Was genau meinst du? Das mit dem statischen "Create" oder dass die Syntax dafür möglichst kurz sein soll? Für das "Create" habe ich jedenfalls einen praktischen Nutzen, ist allerdings etwas aufwändiger zum erklären. Und Delphi kann ich auf jeden Fall mal nicht^^

Ich meine eher das statische Create. Kurz wird's ja schon, wenn ich nur


A myA = new A()

schreibe.

Konstruktoren sind eh statisch. Also wieso diese Umständlichkeiten?

Grüsse,

N1ls
private Nachricht | Beiträge des Benutzers
spike24
myCSharp.de - Member



Dabei seit:
Beiträge: 443
Herkunft: Steiermark Österreich

beantworten | zitieren | melden

Konstruktoren sind eh statisch? ähm ... nein
Es gibt static und instance Konstruktoren
Die statischen haben static davor stehen die anderen einen Accessmodifier (public, protected, internal, private)

@MarsStein
Ja, schön ist das nicht sonderlich (in meiner benutzung dieses constructs lese ich eine Assembly aus und befülle somit das Dictionary mit den elementen ohne sie zu kennen)
Ja die construktoren können internal sein, ging mir nur ums Prinzip.

//Edit, mich hats nicht in Ruhe gelassen
jetzt gibt es nur noch die unsauberkeit des statischen ctors, was ich bei mir ja anders gelöst habe.

  internal interface ICreateable<T> where T : Base<T>
  {
    T CreateInstance();
  }

  public abstract class Base<T> where T : Base<T>
  {

    private static Dictionary<Type, object> mFactories;

    static Base()
    {
      mFactories = new Dictionary<Type, object>();
      mFactories.Add(typeof(A), new A());
      mFactories.Add(typeof(B), new B());
      mFactories.Add(typeof(C), new C());
    }

    public Base()
    {
    }

    public static T Create()
    {
      if (SomeCondition())
      {
        ICreateable<T> factory = (ICreateable<T>)mFactories[typeof(T)];
        return factory.CreateInstance();
      }

      return null;
    }

    private static bool SomeCondition()
    {
      return true;
    }

  }

  public class A : Base<A>, ICreateable<A> // <- <A> nur bei Klassendeklaration nötig
  {

    internal A()
    { }

    A ICreateable<A>.CreateInstance()
    {
      return new A();
    }

  }

  public class B : Base<B>, ICreateable<B> // <- <B> nur bei Klassendeklaration nötig
  {
    internal B()
    { }

    B ICreateable<B>.CreateInstance()
    {
      return new B();
    }

  }

  public class C : Base<C>, ICreateable<C>
  {
    internal C()
    { }

    C ICreateable<C>.CreateInstance()
    {
      return new C();
    }

  }
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von spike24 am .
mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen
private Nachricht | Beiträge des Benutzers
Sarc
myCSharp.de - Member



Dabei seit:
Beiträge: 417

beantworten | zitieren | melden

Zitat von spike24
jetzt gibt es nur noch die unsauberkeit des statischen ctors, was ich bei mir ja anders gelöst habe.
Naja, dass die Basisklasse ihre abgeleiteten Klassen kennt, finde ich persönlich auch etwas "unsauber".
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Sarc am .
private Nachricht | Beiträge des Benutzers
spike24
myCSharp.de - Member



Dabei seit:
Beiträge: 443
Herkunft: Steiermark Österreich

beantworten | zitieren | melden

das meinte ich, schlecht geschrieben.

Könnte man aber über Reflection lösen.
Allerdings müssten dann die abgeleiteten Klassen in der selben Assembly liegen, sonst können sie keinen internal-ctor haben ... ausser man verwendet das Attribut InternalsVisibleToAttribute.
mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen
private Nachricht | Beiträge des Benutzers