Autor Beitrag
_mk_
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Mo 29.08.22 09:16 
Hallo zusammen.

Meine eigene Klasse erbt von System.DirectoryServices.AccountManagement.UserPrincipal. In meinem Konstruktor rufe ich die UserPrincipal.FindByIdentity Methode auf, um meine Klasse mit einem konkreten UserPrincipal-Objekt zu initialisieren. Da ich ich nicht weiß, wie ich auf Eigenschaften der Basisklasse zugreifen kann, kopiere ich bei der Objekterstellung die Eigenschaftswerte der Superklase in meine Klasse. Leider lassen sich so schreibgeschütze Eigenschaften nicht initialisieren.

Was musst ich tun, um meine Klasse mit einem konkreten Object der Basisklasse zu initialiseren?

Vielen Dank im Voraus.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
        public ADUser(PrincipalContext context, string userName) : base(context)
        {
            UserPrincipal userPrincipal = FindByIdentity(context, userName);

            if (userPrincipal != null)
            {
                foreach (PropertyInfo propertyInfo in userPrincipal.GetType().GetProperties())
                {
                    if ((!propertyInfo.CanWrite) && (!propertyInfo.CanRead))
                        continue;

                    object value = propertyInfo.GetValue(userPrincipal, null);
                    if (value != null)
                    {
                        Debug.Print($"Base {propertyInfo.Name} = {value.ToString()}");

                        PropertyInfo piThis = this.GetType().GetProperty(propertyInfo.Name);
                        if (piThis != null && piThis.CanWrite)
                        {
                            Debug.Print($"This {piThis.Name} = {value.ToString()}");
                            piThis.SetValue(this, value, null);
                        }
                        else
                        {
                            Debug.Print("    ReadOnly!");
                        }

                        Debug.Print(new string('-'20));
                    }
                }
            }
        }
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4706
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mo 29.08.22 11:03 
Gefühlt ist Ableitung eine Sackgasse. Bist du dir sicher das du das brauchst? Das ist übrigens auch allgemein so. Meist ist Ableitung der falsche Weg und man sollte über eine Komposition nachdenken.
Jeder denkbare Einsatz einer Ableitung fühlt sich hier für mich eher nach Mißbrauch an. Z.b. um zusätzliche Daten in irgendwelchen Contexten in einem Anwendung weiterzuverteilen (z.b. in den verschiedenen Asp.Net Varianten in den UserContexten, HttpContexten, Claims oder was gerade aktuell ist) ohne eine eigene Infrastruktur dafür zu schaffen.

Das System wird dir immer einen UserPrincipal (oder Ableitung davon) liefern nie deine Version. Und das System wird nie lernen mit deiner Version umzugehen (außer mit den Teilen die UserPrincipal darstellen). Alle Daten umzukopieren in etwas das dann scheinbar auch ein UserPrincipal ist aber eine Ableitung heißt auf Interna zuzugreifen die sich jederzeit ändern können (darum gibt es unter anderem sowas wie Interna damit es auch im Zweifel änderbar ist ohne das es Einfluss auf die Nutzer der Klasse hat). Bin mit dem AccountManagement auch nicht ganz vertraut bin mir aber ziemlich sicher das hinter einem Principal ein Kernelobjekt steckt und dessen Handle zu kopieren so das das überlebt auch wenn die original Instanz die du vom System bekommen hast stirbt ist bestimmt ein eigenes kleines Abenteuer.

Für diesen Beitrag haben gedankt: Th69, _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Mi 31.08.22 08:24 
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Gefühlt ist Ableitung eine Sackgasse. Bist du dir sicher das du das brauchst? Das ist übrigens auch allgemein so. Meist ist Ableitung der falsche Weg und man sollte über eine Komposition nachdenken.

Ich werde noch einmal in mich gehen, ob ich es nicht doch anders mache. Was meinst Du mit Komposition? Dieser Begriff ist mir neu.

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
bin mir aber ziemlich sicher das hinter einem Principal ein Kernelobjekt steckt

Wie finde ich heraus, ob sich hinter einem Objekt ein Kernelobjekt steckt und welche sonstige Gefahren verbergen sich dahinter?
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4706
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 31.08.22 12:57 
Komposition an Stelle von Vererbung

Deine neue gedachte Klasse soll keine Ableitung von UserPrincipal sein sondern sie hat einen UserPrincipal. Keine Vererbung sondern eine Beziehung zwischen 2 Klassen in welcher Form auch immer. Das nennt man dann Komposition.

Hier aus "Die Klasse ist ein UserPrincipal" wird "Die Klasse hat einen UserPrincipal";

Für diesen Beitrag haben gedankt: _mk_
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4706
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 31.08.22 16:04 
2.te Frage übersehen.

Kann man nicht so einfach.
- man kann sich durch den Framework Source code quälen und versuchen den zu verstehen, denn das problematische ist nichts was veröffentlicht werden wird. Das Framework will ja an der Oberfläche systemunabhängig sein da macht es dann keinen Sinn systemabhängige Internas zu veröffentlichen. Ein UserPrincipal z.b. funktioniert unter Linux sicher anders als unter Windows auch wenn man das gleiche Framework verwendet.
- Es ist manchmal offensichtlich weil die Klasse Handles veröffentlicht ist aber eher ein seltener Fall
- man hat Erfahrung und weiß das bestimmte Dinge eigentlich Windows Interna sind und nur durch das Freamework veröffentlich werden
- oder man achtet darauf ob Dinge disposable sind. Ist vermutlich in den meistgen Fällen der beste Marker. Bei Klassen die IDisposable implementieren hängen da üblicherweise Dinge dran die nicht vom Framework kontrolliert werden (und darum durch dispose expliziert zerstört werden müßen weil der Garbage Collector des Frameworks von diesen "fremden" Dingen nix weiß. Da ist kopieren immer ein Problem und man müßte sich dann nach dem kopieren fragen ist das disposen jetzt ein Problem. Was passiert wenn ich Original und Kopie dispose.

Gefühlt ist mir noch nie eine Klasse untergekommen die eine "offizielle" Kopiermöglichkeit implementiert hat und Disposable war.

Für diesen Beitrag haben gedankt: _mk_
_mk_ Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 23



BeitragVerfasst: Mo 05.09.22 07:40 
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Komposition an Stelle von Vererbung

Deine neue gedachte Klasse soll keine Ableitung von UserPrincipal sein sondern sie hat einen UserPrincipal. Keine Vererbung sondern eine Beziehung zwischen 2 Klassen in welcher Form auch immer. Das nennt man dann Komposition.

Hier aus "Die Klasse ist ein UserPrincipal" wird "Die Klasse hat einen UserPrincipal";


Aus dem Wikipedia-Eintrag werde ich nicht ganz schlau. Auch meine "schlauen" Bücher helfen mir da nicht wirktlich weiter. Das Thema Komposition wird leider nicht behandelt. Wie setze ich diese Technik um? Könnt Ihr es bitte an meiner Klasse verdeutlichen? Definiere ich das UserPrincipal-Objekt als Eigenschaft in meiner Klasse?

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:

- man hat Erfahrung und weiß das bestimmte Dinge eigentlich Windows Interna sind und nur durch das Freamework veröffentlich werden
- oder man achtet darauf ob Dinge disposable sind. Ist vermutlich in den meistgen Fällen der beste Marker. Bei Klassen die IDisposable implementieren hängen da üblicherweise Dinge dran die nicht vom Framework kontrolliert werden (und darum durch dispose expliziert zerstört werden müßen weil der Garbage Collector des Frameworks von diesen "fremden" Dingen nix weiß. Da ist kopieren immer ein Problem und man müßte sich dann nach dem kopieren fragen ist das disposen jetzt ein Problem. Was passiert wenn ich Original und Kopie dispose.

Ich muss noch viele Erfahrungen mit C# sammeln. Für mich reicht erst einmal die Aussage, so bald die Schnittstelle IDisposable von der Klasse implementiert wurde, dass es sich um ein "Kernelobjekt" handelt.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4706
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mo 05.09.22 08:27 
Wir kennen nur den Konstruktor deiner Klasse. Das ist definitiv zu wenig für einen sinnvollen Vorschlag. Und wir Wissen auch nicht wofür du diese Klasse benutzen willst.

Ohne jeglichen Kontext kann ich nur ein Standardkonstrukt vorschlagen und der wäre das du die zu verwendende UserPrincipal Instanz an den Konstruktor übergibst und dir dann in einem Feld/einer Property mit passender Sichtbarkeit merkst. Eher simpel.

Für diesen Beitrag haben gedankt: _mk_
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4791
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 05.09.22 08:55 
Komposition bedeutet Zusammensetzung oder auch Aufbau (s.a. die Übersetzung für das englische Wort composition), d.h. dies ist der Normalfall beim Erstellen einer Klasse:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
class X
{
  public string Text; // oder auch als Eigenschaft { get; set; }
  public int Value;   // dito
}

Die Klasse X ist also zusammengesetzt aus den beiden Feldern (bzw. Eigenschaften).
Und daher spricht man hier von "Die Klasse X hat einen Text und einen Value.", anstatt bei einer Vererbung von "Die Klasse X ist ein Text (oder Value).".

PS: Das Wort "Kernelobjekt" ist etwas zu speziell für IDisposable: ich würde eher von "externer Ressource" sprechen.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 05.09.22 09:33 
Komposition anstelle von Vererbung ist nicht immer ganz einfach ;)

Zuerst einmal musst Du dir überlegen, warum Du die Vererbung brauchst.
Man kann sie nicht immer auflösen, z.B. wenn eine externe Funktion nur mit Hilfe einer Ableitung beeinflusst werden kann, dann ist das gut und auch richtig so.
Aber wenn Du die Basisklasse nur brauchst, um Funktionen der Basisklasse auch in der Ableitung zu nutzen oder weil Du faul bist, dann kann man das ganz gut aufteilen.
Es gibt aber auch komplexe Fälle, wo man ganz bewusst auf komplexe Vererbung setzt (z.B. UI-Frameworks), aber das hier einmal außenvor.

Der Grund, warum man auf Vererbung möglichst verzichten sollte:
Es ist die mit Abstand schlimmste Abhängigkeit, die Du dir in deinen Code prügeln kannst ;)
Ich hatte den Fall schon, dass ich eine Vererbungs-Hierarchie wieder auflösen musste - ist kein Spaß, sag ich dir.

Wenn Du also eine Klasse hast, die Funktionen der Basis-Klasse brauchst, dann überlege dir, ob Du die Funktionen der Basisklasse auch auslagern kannst.
Z.B. beim Standard-Beispiel Auto: Ein Auto braucht Motor, Lenkung, Schaltung, viel Elektronik, etc. etc.
Nach dem Standard-Beispiel würde man hingehen und eine Basis-Klasse "Fahrzeug" mit einem dieser Funktionen schreiben und "Auto" leitet davon ab. Und dann hast Du ein Flugzeug - wie bringst Du das in deine Struktur ohne von den ganzen anderen Dingen, die in "Fahrzeug" definiert sind und die Du nicht brauchst, abhängig zu sein? Kurz: Gar nicht. Entweder Du akzeptierst das - was meistens eine dumme Idee ist - oder baust alles um - was meistens auch eine dumme Idee ist, aber zumindest etwas besser.

Oder Du greifst zur Alternative mit Hilfe von Komposition:
Viele verschiedene Klassen für jede Teil-Funktion, also "Motor", "Lenkung", "Schaltung", etc.
Die Klasse "Auto" hat dann keine Basis-Klasse mehr, aber erstellt Instanzen dieser Klassen für Teil-Funktionen und nutzt sie.
Auf diese Weise kannst dir ein Auto Teil für Teil im Code zusammen bauen und wenn Du ein Flugzeug hast, nimmst Du eben nur das, was Du brauchst und für den Rest entwickelst Du nach dem gleichen Konzept Klassen.

Folgendes ...

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
public abstract class Fahrzeug
{
    public abstract void Fahre();

    protected void StartMotor()
    {
        // ...
    }
}
public class Auto : Fahrzeug
{
    public override void Fahre()
    {
        // ...
        StartMotor();
        // ...
    }
}


... wird zu ...

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
public class Motor
{
    public void Start()
    {
        // ...
    }
}
public class Auto
{
    private readonly Motor _motor;

    public Auto()
    {
        _motor = new Motor();
    }

    public void Fahre()
    {
        // ...
        _motor.Start();
        // ...
    }
}


Und wenn Du die Vorteile der Vererbung (gemeinsamer Basis-Typ) nutzen willst, kannst Du auch ein Interface erfinden und das implementieren.

ausblenden C#-Quelltext
1:
2:
3:
4:
public interface IAuto
{
    void Fahre();
}


Und siehe da, Du hast die gleichen Vorteile, nur ohne die Nachteile ;)

Es kann aber vorkommen, dass Du eine Funktion hast, die in sich zwar unabhängig ist (z.B. Motor), sich je Situation aber anders verhält (Auto-Motor, Flugzeug-Motor).
In dem Fall wäre Vererbung wieder OK, dann leitest Du in "AutoMotor" und "FlugzeugMotor" einfach von "Motor" ab und nutzt dann in "Auto" bzw. "Flugzeug" deine Ableitung.


Der nächste Schritt, wenn das einmal begriffen ist, wäre, wie Du auch noch diese Abhängigkeit (Auto->AutoMotor) auflösen kannst.
Das geht dann mit Hilfe von Interfaces und Dependency Injection, dann ist "Auto" nicht mehr direkt von "AutoMotor" abhängig, sondern nur noch indirekt von einem Interface "IMotor" und die Implementierung dazu (VerbrennerMotor oder Elektromotor) wird von außen über den Konstruktor sozusagen eingebaut.
Der Vorteil ist, dass Du die Auto-Klasse für viele verschiedene Dinge (Verbrenner-Auto, Elektro-Auto, Hybrid-Auto, Seifenkiste) nutzen kannst, ohne nur einmal den Code dafür anzupassen.

Aber wie gesagt: Eins nach dem Anderen ;)

Für diesen Beitrag haben gedankt: _mk_
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 05.09.22 09:40 
Zweite Nachricht, wegen der Übersicht

Was dein konkretes Ziel ist, weiß ich nicht.
Aber ich rate mal in den Raum, was Du für das UserPrincipal-Objekt eigene Funktionen hast, die Du im Programm nutzen willst.
Also eigentlich brauchst Du gar keine Vererbung :P

Stattdessen könntest Du folgendes machen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
public interface IUserContext
{
    string Name { get; }

    void MeineFunktion();
}
public class UserContext : IUserContext
{
    private readonly UserPrincipal _principal;

    public string Name
    {
        get
        {
            return ...;
        }
    }

    public UserContext(UserPrincipal principal)
    {
        _principal = principal;
    }

    public void MeineFunktion()
    {
        // ...
    }
}


Das Interface, damit dein ganzes Programm nicht von dieser einen Klasse abhängig ist.
Das Thema hab ich vorhin am Ende kurz angerissen - macht z.B. UnitTests erst möglich, weil Du da kein UserPrincipal hast.

Jede deiner anderen Klassen und Methoden nutzt dann also das "IUserContext"-Interface.
Du baust beim Login eine "UserContext"-Instanz und gibst sie entsprechend weiter.
So kannst Du deine zusätzlichen Funktionen für das UserPrincipal um das UserPrincipal herum bauen, ohne problematische Vererbung und man kann es sogar noch gut in UnitTests testen.

Für diesen Beitrag haben gedankt: _mk_