Laden...

Wie setzt man Property Value per Reflection richtig?

Erstellt von PierreDole vor einem Jahr Letzter Beitrag vor einem Jahr 600 Views
P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor einem Jahr
Wie setzt man Property Value per Reflection richtig?

Moin,

ich schreibe gerade einen kleinen Reflection Wrapper und komme beim Setzten von Property Values nicht weiter. Es kommt immer diese Exception:

System.ArgumentException: 'Property set method not found.'

Ich habe mich vergewissert, daß das Property, an dem ich den Wrapper ausprobiere, Getter und Setter hat. Mein einziger Anhaltspunkt ist, daß ich beim "Source Object" was falsch mache. In allen Beispielen, die ich finde, wird als Source Object eine zuvor erstellte Instance einer Klasse genommen, die dann der Methode SetValue übergeben wird, wie z.B. hier https://learn.microsoft.com/de-de/dotnet/api/system.reflection.propertyinfo.setvalue?view=net-7.0

...
Example exam = new Example();
...
PropertyInfo piInstance = examType.GetProperty("InstanceProperty");
piInstance.SetValue(exam, 37);
...

Ich habe aber keine solche Klasse, da sie selbst per Reflection instanziert wird. Ist das richtig, daß ich in diesem Fall diese Instanz als Parameter übergebe? (Alles andere funktioniert.)

Das ist der Wrapper:

    public class Reflector
    {
        private Assembly _assembly;
        private Type _type;

        private object _instance;
        public object Instance { get => _instance; }


        public Reflector(Assembly assembly) =>
            _assembly = assembly;


        public Type SetClass(string className) =>
            _type = _assembly.GetType(className);

        public object CreateInstance(params object[] args) =>
            _instance = Activator.CreateInstance(_type, args);


        public T GetPropertyValue<T>(string propertyName, BindingFlags bindingFlags) =>
            (T)_type.GetProperty(propertyName, bindingFlags).GetValue(_type, null);

        public T GetPropertyValue<T>(string propertyName) =>
            (T)_type.GetProperty(propertyName).GetValue(_type, null);


        public void SetPropertyValue(string propertyName, object value, BindingFlags bindingFlags) =>
            _type.GetProperty(propertyName, bindingFlags).SetValue(_instance, value);

        public void SetPropertyValue(string propertyName, object value) =>
            _type.GetProperty(propertyName).SetValue(_instance, value);


        public T InvokeMethod<T>(string methodName, BindingFlags bindingFlags, params object[] args) =>
            (T)_type.GetMethod(methodName, bindingFlags).Invoke(_instance, args);

        public T InvokeMethod<T>(string methodName, params object[] args) =>
            (T)_type.GetMethod(methodName).Invoke(_instance, args);

        public void InvokeMethod(string methodName, BindingFlags bindingFlags, params object[] args) =>
            _type.GetMethod(methodName, bindingFlags).Invoke(_instance, args);

        public void InvokeMethod(string methodName, params object[] args) =>
            _type.GetMethod(methodName).Invoke(_instance, args);
    }
T
2.224 Beiträge seit 2008
vor einem Jahr

Klingt erstmal so als gibt es bei der Property keinen setter.
Ergo ist dies eine Eigenschaft, die du von außen nur lesen kannst.
Oder kennst du die konkrete Klasse und weißt, dass dem nicht so ist?

Ansonsten ist das Beispiel von Microsoft recht klar.
Du musst die Instanz zum setzen auch übergeben, wie sonst sollte .NET wissen wo du die Eigenschaft mit dem Wert setzen willst?

Mir ist aber nicht klar warum du so einen Umweg machst.
Warum kannst du den Typen nicht direkt in deinem Projekt instanzieren?
Gibt es hier bestimmte Gründe dafür?
Falls nicht, dann schenk dir den Umweg mit Reflection, binde die Assembly direkt ein und instanzieren den konkreten Typen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor einem Jahr

Ich habe keinen direkten Zugriff auf die Klassen. Ich arbeite an einem Plugin für einen Ultima Online Client. ClassicUO, heißt er, wurde in C# geschrieben und ist Open Source. Das Plugin wird vom Client geladen, also lade ich die Assembly vom Client und bekomme Zugriff auf seine Klassen. Will mir die GUI etwas anpassen.

Ich kann Gumps erstellen, sie befüllen und zurück an den UIManager des Clients übergeben. Sie werden auch im Spiel angezeigt. Auch die Button Klasse, um die es oben geht, kann ich mit dem Reflector instanzieren und sie wird angezeigt. Aber halt bei der Koordinate 0, 0. Sobald ich X setze, kracht es.

Hier ein Beispiel, wie der Client die Klasse instanziert:

                Add
                (
                    new Button((int)Buttons.Help, 0x07ef, 0x07f0, 0x07f1)
                    {
                        X = 185,
                        Y = 44 + 27 * 0,
                        ButtonAction = ButtonAction.Activate
                    }
                );

Und so greife ich drauf zu:

    public class Button : Control
    {
        public int X
        {
            get => Reflector.GetPropertyValue<int>("X");
            set => Reflector.SetPropertyValue("X", value);
        }

        public int Y
        {
            get => Reflector.GetPropertyValue<int>("Y");
            set => Reflector.SetPropertyValue("Y", value);
        }

        public int ButtonAction
        {
            get => Reflector.GetPropertyValue<int>("ButtonAction");
            set => Reflector.SetPropertyValue("ButtonAction", value);
        }

        // Die Args habe ich vom Client copy-pastet.
        public Button
        (
            int buttonID,
            ushort normalGraphic,
            ushort pressedGraphic,
            ushort overGraphic = 0,
            string caption = "",
            byte font = 0,
            bool isUnicode = true,
            ushort normalHue = ushort.MaxValue,
            ushort hoverHue = ushort.MaxValue
        )
        {
            Reflector.SetClass("ClassicUO.Game.UI.Controls.Button");
            Reflector.CreateInstance(buttonID, normalGraphic, pressedGraphic, overGraphic, caption, font,
                isUnicode, normalHue, hoverHue);
        }
    }
			// GumpPic, Gump und UIManager sind, wie die Button-Klasse "reflectiert".
            var pic = new GumpPic(0, 0, 0x07d0, 0);
            var button = new Button(1, 0x07ef, 0x07f0, 0x07f1);
            button.X = 185; // Hier kracht's. Ohne diese Zeile wird alles angezeigt.
            
            var gump = new Gump();
            gump.Add(pic);
            gump.Add(button);

            UIManager.Add(gump);
2.080 Beiträge seit 2012
vor einem Jahr

Dein Code:

(T)_type.GetProperty(propertyName).GetValue(_type, null);

Warum übergibst Du _type als Instanz? Das müsste _instance sein.

Und bist Du sicher, dass Du keine BindingFlags brauchst? Das brauchst Du eigentlich nur dann nicht, wenn alles public ist.

Und debugge mal rein und schau dir an, ob die PropertyInfo eine SetMethode-Instanz hat.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor einem Jahr

Ja, das hätte _instance sein müssen. Ist korrigiert.

BindingFlags brauche ich in diesem Fall tatsächlich nicht, da die Properties und Methoden public sind. Die Klassen sind zwar internal, aber das spielt keine Rolle. Der Zugriff drauf ist der gleiche, wenn ich das richtig gelesen habe. Ich hab es trotzdem mit Public und NonPublic BindingFlags versucht und es wurde die selbe Exception geworfen.

Habs debugt, fand aber keine Info zu Gettern und Settern, nur die Auflistung der Properties mit Bezeichner und Type. Aber ich habe mir den Client Code noch einmal angeschaut und ich weiß nicht, was ich gestern da zur späten Stunde gesehen habe, aber heute fand ich das in der geerbten Klasse des Buttons:

public ref int X => ref _bounds.X;

Kein Setter! _bounds ist ein Rectangle vom Microsoft.Xna Framework, auf dem der Client basiert. Muss ich jetzt mein Vorhaben begraben oder kann ich die Referenz mit Reflection irgendwie rüberholen?

L
155 Beiträge seit 2012
vor einem Jahr
public ref int X => ref _bounds.X;

Wenn du weißt dass die Property nur ein Fascade für ein zugrundeliegendes Objekt ist, kannst du via Reflection auch die das Object _bounds einfach direct holen und prüfen ob deren X Property ein Setter hat. Ggf. wenn es weitere Objekte gibt hangelst du dich durch. Sollte Xein Auto-Property sein erzeugt der Compiler automatisch ein Feld nach einem bestimmten Namensschema (das kannst du dir i Debugger auslesen). Das Feld solltest du via Reflection schreiben können.

Mit freundlichen Grüßen
lutzeslife

2.080 Beiträge seit 2012
vor einem Jahr

Ich hab es trotzdem mit Public und NonPublic BindingFlags versucht und es wurde die selbe Exception geworfen.

Ich persönlich schreibe auch immer noch instance dazu.
Ob das notwendig ist, oder ob er von alleine davon ausgeht, weiß ich aber nicht.

Muss ich jetzt mein Vorhaben begraben oder kann ich die Referenz mit Reflection irgendwie rüberholen?

Du kannst auch einfach das `_bounds` Feld suchen und setzen.
Aber bedenke: Das sind Details, die sich gerne mal ändern, natürlich würde das bei dir dann knallen.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor einem Jahr

Ich habe die Reflector Klasse um GetFieldValue erweitert und versucht _bounds auszulesen. Es wurde ständig die NullReferenceException geworfen, mit der Meldung: Object reference not set to an instance of an object. Im Debug-Mode war aber alles gesetzt, wie es sollte.

Meine Güte, was habe ich mich gerade totgesucht. Aber, zu erst, das hier ist _bounds:

private Rectangle _bounds;

Nach viel aufsteigendem Rauch aus meinem Kopf, fiel mir ein, _bounds befindet sich in der Klasse Control, die Button erbt und ist somit für Button nicht sichtbar. Ich sätze, da kommt man auch mit Reflection nicht ran, oder?

Ich persönlich schreibe auch immer noch instance dazu.

Das wäre meine Nebenfrage gewesen. Da ich im Reflector jede Methode jeweils mit BindingFlags-Parameter überlade, macht es Sinn, um sich die Überladungen zu sparen, wenn ich den hier mache...

private readonly BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
            | BindingFlags.Static;

... und das überall per default übergebe? Performance-Einbrüche würde das nicht verursachen, oder?

2.080 Beiträge seit 2012
vor einem Jahr

Probier mal BindingFlags.FlattenHierarchy

Hab ich selber noch nie genutzt, kann daher nicht sagen, ob das hilft, aber ein Versuch ist es wert.
Allerdungs hast Du dann das gleiche Problem mit den Fields nur noch größer, da es über mehrere Klassen ja auch mehrere private Fields mit dem selben Namen geben kann.

Performance-Einbrüche würde das nicht verursachen, oder?

Rein logisch würde ich sagen: Ja.
Aber Microsoft hat da in letzter Zeit so viel optimiert, dass ich mir nicht sicher bin, ob das überhaupt relevant ist.
Du darfst ja auch nicht vergessen, dass Reflection generell eher langsam ist, auch wenn's besser geworden ist.
Also vergiss erst einmal die Performance und miss hinterher, ob das für dich in Ordnung ist.

Mit Methoden (also auch Properties) hätte ich gesagt, erstelle einen Delegaten (Delegate.CreateDelegate), das sollte die beste Optimierung für Reflection sein, aber es gibt keine Überladung für Felder, das fällt also raus.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

L
155 Beiträge seit 2012
vor einem Jahr
 internal class Rectangle
    {
        public int X { get; set; }
    }

    internal class Control
    {
        private Rectangle _bounds;

        public Control()
        {
            _bounds = new Rectangle { X = 5 };
        }

        public int X => _bounds.X;
    }

    internal class Button : Control
    { 
    
    }

Habe es mal kurz durchgespielt

  static void Main(string[] args)
        {
            var button = new Button();

            Console.WriteLine($"X (OLD): {button.X}");

            // Basisklasse öffentlich, Rectangle Klasse öffentlicht

            var bounds = (Rectangle)typeof(Control)
                                .GetField("_bounds", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                                .GetValue(button);
            bounds.X = 6;

            Console.WriteLine($"X (NEW): {button.X}");


            //  Basisklasse nicht öffentlich, Rectangle Klasse nicht öffentlicht

            var boundsOBJ = button
                            .GetType()
                            .BaseType // Rekursiv suchen ggf.
                            .GetField("_bounds", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                            .GetValue(button);

            // Autoproperties bekommen einen autogenierten Namen der __BackingField enthält
            boundsOBJ.GetType().GetField("<X>k__BackingField", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(boundsOBJ, 20);

            Console.WriteLine($"X (NEW) WAY 2: {button.X}");

            Console.ReadLine();
            
        }

Im wesentlichen  kannst du alles was öffentlich ist auch nutzen für Reflection ansonsten muss man es eben generisch machen. Mit Reflection bekommst du fast alles geliefert und kannst auch Änderungen vornehmen (mit Ausnahmen).  Eventuell falls du Reflection häufig brauchst würde ich über Caching der typen etc. nachdenken. Es gibt einige Blogbeiträge zum Thema Performance bei Reflection.

Mit freundlichen Grüßen
lutzeslife

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor einem Jahr
public int X => _bounds.X;

Die beiden sind im Client Code jeweils mit ref versehen.

public ref int X => ref _bounds.X;

Ich werde den zweiten Teil deines Code einmal mit "Rectangle _bounds" ausprobieren.

Probier mal BindingFlags.FlattenHierarchy

Hat leider nicht funktioniert. Es wird weiterhin die NullReferenceException geworfen.

Aber Microsoft hat da in letzter Zeit so viel optimiert, dass ich mir nicht sicher bin, ob das überhaupt relevant ist.

In "letzter Zeit" hört sich schon mal schlecht an, da ich mit .Net Framework 4.8 unterwegs bin. Der Client erfordert ein Assemby Objekt von den Plugins und wie es aussieht, funktioniert nur ein Framework 4.8 Assembly. Ich es habe anfangs mit .Net 7 versucht und das Plugin wurde nicht angenommen. Leider gibt es überhaupt keine Dokumentation zu Plugins und man muss sich alles selbst aus dem Client Code und aus den paar existierenden Plugins erlesen. Habe mich schon gefragt, ob man dem Client nicht irgendwie ein .Net 7 Assembly als Framework 4.8 "vorgaukeln" könnte. Denn dann könnte ich sogar unter Linux arbeiten. Habs aber nicht geschafft und aufgegeben.

Du darfst ja auch nicht vergessen, dass Reflection generell eher langsam ist, auch wenn's besser geworden ist.

Wenn das so ist, dann sollte ich meine Herangehensweise noch einmal überdenken. Denn ich werde Performance brauchen. Ein custom Inventory anzuzeigen wäre leicht verzügert ja noch verträglich. XX Items auf einen Schlag dort einfügen, wahrscheinlich eher nicht. GumpPics und Buttons (die Items sind klickbar und das wird mit "getarnten" Buttons realisiert) rüberholen, erstellen und dann wieder zum Client zurückschicken. Ich weiß nicht, ob diese Prozedur, bestehend aus vielen Aufrufen von Methoden und Setzen von Property Values via Reflection, nicht den Client zum Laggen bringt. Und dann noch die Cooldown Bars, die ich machen wollte, die alle 1/10 Sekunde akutalisiert werden sollten.

Um einmal kurz laut zu denken:

Eigentlich wollte ich den Client Code nicht anfassen, aber ich werde den Verdacht nicht los, als käme ich nicht herum. Ich könnte eigene Klassen dort platzieren, die das alles erzeugen und steuern und sie dann per Reflection aufrufen. Das würde wiederum die Delegates interessant machen. Aber ich verstehe nicht, wie man ein Delegate mit einer "reflektierten" Methode / Property verknüpfen kann.

2.080 Beiträge seit 2012
vor einem Jahr

Die beiden sind im Client Code jeweils mit ref versehen.

Sollte eigentlich keinen Unterschied machen, da es dir ja um das Feld dahinter geht und nicht um die Property.

Aber Du kannst mal Reflection Code ohne deine Abstraktion in einem kleinen Test-Projekt bauen und dann hier posten.
Und schreib auch noch die Definitionen von der Ziel-Klasse und den Membern dazu, um die es dir geht.

ob man dem Client nicht irgendwie ein .Net 7 Assembly als Framework 4.8 "vorgaukeln" könnte

Nö - beides ist genau gar nicht miteinander kompatibel.
Für Kompatibilität zwischen beiden gibt .NET Standard, aber das ist nur eine Abstraktion der Framworks, hilft also auch nicht.
Ggf. kriegt man es dazu, es aufzurufen, ist ja auch nur IL-Code. Aber ob es dann auch wirklich durchgehend funktioniert, weißt Du immer noch nicht, die werden dadurch ja nicht plötzlich kompatibel zueinander.

Denn ich werde Performance brauchen

Dann teste es, Optimierungen aber erst hinterher.
Was Du aber optimieren kannst (und ggf. solltest) ist das, was lutzeslife schreibt: Cache

Aber ich verstehe nicht, wie man ein Delegate mit einer "reflektierten" Methode / Property verknüpfen kann.

var myMethodInfo = ...
var myDelegate = (Func<MyControl, bool>)Delegate.CreateDelegate(typeof(Func<MyControl, bool>), myMethodInfo);
var myBool = myDelegate(myControl);

Das funktioniert aber nur, wenn Du auch eine MethodInfo (bei einer Property dann der Getter bzw. Setter) bekommst, was bei einem Feld nicht der Fall wäre.
Für Felder kenne ich keine vergleichbare Alternative, außer Caching.

Du könntest IL-Code generieren und aufrufen, ich weiß aber nicht, ob das mit private membern funktioniert. Außerdem ist das ziemlich aufwändig, sowohl für dich, als auch zur Laufzeit und ob es wirklich schneller ist, ist auch fraglich, daher: Erst zuende arbeiten, dann testen, dann optimieren

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor einem Jahr

Ich habe das oben einmal alles durchprobiert und das Ergebnis ist immer noch die NullReferenceException. Aber ich konnte den Fehler zumindest etwas eingrenzen.

Dieser Teil des Codes hat immer funktioniert:

            int buttonID = 1;
            ushort normalGraphic = 0x07ef;
            ushort pressedGraphic = 0x07f0;
            ushort overGraphic = 0x07f1;
            string caption = "";
            byte font = 0;
            bool isUnicode = true;
            ushort normalHue = ushort.MaxValue;
            ushort hoverHue = ushort.MaxValue;

            Type type = Engine.ClassicAssembly.GetType("ClassicUO.Game.UI.Controls.Button");
            object button = Activator.CreateInstance(type, buttonID, normalGraphic, pressedGraphic, overGraphic,
                caption, font, isUnicode, normalHue, hoverHue);

            FieldInfo boundsInfo = type.BaseType.GetField("_bounds", BindingFlags.NonPublic | BindingFlags.Instance);
            object bounds = boundsInfo.GetValue(button);

Heißt, die instance von _bounds war immer als Rectangle gesetzt.

Das Nachfolgende war dann die Trail&Error-Phase, unterbrochen durch viel Suchen und Lesen. Ich habe die gescheiterten Versuche dringelassen und auskommentiert, damit sie nachvollzogen werden können. Am Ende der Zeilen stehen die Debug-Ergebnisse.

            // 1
            //FieldInfo xkInfo = bounds.GetType().GetField("<X>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); // null
            
            // 2
            //FieldInfo xInfo = bounds.GetType().GetField("X", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); // gesetzt
            //bool hasBackingField = xInfo.IsDefined(typeof(CompilerGeneratedAttribute), false); // false
            //FieldInfo xkInfo = xInfo.DeclaringType.GetField("<X>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); // null

            // 3
            // FieldInfo xkInfo = bounds.GetType().GetField("<Left>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); // null
            
            // 4
            PropertyInfo leftInfo = bounds.GetType().GetProperty("Left", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); // null
            bool hasBackingField = leftInfo.IsDefined(typeof(CompilerGeneratedAttribute), false); // false
            FieldInfo xkInfo = leftInfo.DeclaringType.GetField("<Left>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); // null

            // hier krachte es immer bei SetValue, da xkInfo bei allen Versuchen null war.
            xkInfo.SetValue(bounds, "10");
  1. So war es gedacht. Direkter Zugriff von _bounds auf das X-BackingField. Hat nicht funktioniert, xkInfowar null.
  2. Hab paar Beispiele gefunden, wo über PropertyInfo auf das BackingField zugegriffen wurde. Ein Versuch war es wert. Habe wieder ewig gebraucht um festzustellen, daß _bounds.X kein Property ist, sondern ein public Field. Zumindest war xInfogesetzt, aberxkInfowar null. Habe aber noch den hasBackingField bool eingefügt, der anzeigen soll, ob der Compiler ein BackingField erzeugt hat. Ich weiß nicht, inwieweit das funktioniert, war immer false.
  3. Irgendwann ist mir aufgefallen, daß Rectangle auch noch ein Left Property hat, das im Getter stumpf X returnt, aber ohne Setter daherkommt. Also das Spielchen nochmal von vorne. Zuerst direkt über _bounds...
  4. ... und dann der Umweg über PropertyInfo. AberxkInfowar auch hier immer null.

Edit:

Stand ich gerade auf dem Schlauch! Beim Durchlesen meines Post ist es mir dann aufgefallen. X ist ein Field, also nicht das BackingField versuchen auszulesen, sondern gleich X setzen! Habe ja noch dazugeschrieben, daß es gesetzt war. Meine Güte, manchmal kann man sich echt selbst auf dem Fuß stehen.


            FieldInfo xInfo = bounds.GetType().GetField("X", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); // gesetzt
            xInfo.SetValue(bounds, 10);

Das ging jetzt.

2.080 Beiträge seit 2012
vor einem Jahr

Hier der Source - hättest Du auch selber raus suchen und posten können 😉

https://github.com/ClassicUO/ClassicUO/blob/main/src/ClassicUO.Client/Game/UI/Controls/Button.cs

https://github.com/ClassicUO/ClassicUO/blob/main/src/ClassicUO.Client/Game/UI/Controls/Control.cs

Und das Rectangle (bin mir nicht sicher, ob das das richtige Repository ist):

https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Rectangle.cs

Alles inklusive dem Abruf des_boundsFeldes funktioniert, richtig?
Und Du möchtest danach Werte vomRectangleabrufen und setzen?

  1. ist das ein Struct, die Werte kannst Du zwar ändern, aber es bringt dir nix, weil es kein Referenz-Typ ist.
  2. X,Y,Widthund Heightsind Felder, keine Properties,GetProperty wird also immernullzurückgeben.
  3. Alle anderen Properties haben kein Backing-Field, sie berechnen den Wert direkt,GetFieldwird also immernullzurückgeben.

Du musst denRectangleWert abrufen, einen neuenRectangleWert erstellen und den dann setzen.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.