Laden...

Veränderung an Objekt in asynchroner Methode im aufrufenden Thread nicht sichtbar

Erstellt von blackdynamic vor 12 Jahren Letzter Beitrag vor 12 Jahren 1.706 Views
B
blackdynamic Themenstarter:in
51 Beiträge seit 2010
vor 12 Jahren
Veränderung an Objekt in asynchroner Methode im aufrufenden Thread nicht sichtbar

Hallo liebe Community,

ich habe folgendes Problem:
Wenn ich eine Eigenschaft eines Objektes, welches sich in einer Liste von Objekten befindet, während einer asynchron laufenden Methode verändere, bekommt der Thread, welcher die asynchrone Methode aufgerufen hat, die Veränderung an dem Objekt nicht mit.
Könnt ihr mir sagen was ich dafür verändern muss?

Hier ein kleines Beispiel:

ViewModel.cs



public ObservableCollection<Person> Persons;

...

public void onAddPerson(object sender, EventArgs e)
{
// Der Name der Person wird im Konstruktor auf Olaf gesetzt. Ob man das so machen würde
// sei dahin gestellt, es geht nur darum meine Situation an einem Beispiel zu
// verdeutlichen
var person = new Person();
Persons.Add(person);

var request = new ChangePersonRequest(person);

// Der asynchrone Aufruf erfolgt über einen Backgroundworker
_asyncService.invokeChangePerson(request, changePersonCallback);
}

private void changePersonCallback(ChangePersonResponse response)
{
// An dieser Stelle ist der Name der Person in der Liste immernoch Olaf.
// Die Person soll aber den Namen haben, der ihr in der asynchronen Methode zugewiesen wurde
persons[0].Name
}

Service.cs


....
public ChangePersonResponse ChangePerson(ChangePersonRequest request)
{
request.CurrentPerson.Name = "Max";

var response = new ChangePersonResponse(request.CurrentPerson);

return response; 
}

ChangePersonResponse.cs


....
public class ChangePersonResponse
{
public Person CurrentPerson {get; set;}

public ChangePersonResponse(Person person)
{
CurrentPerson = person
}
}

ChangePersonRequest.cs


public class ChangePersonRequest
{
public Person CurrentPerson {get; set;}

public ChangePersonRequest(Person person)
{
CurrentPerson = person
}
}

Person.cs


public class Person
{
public string Name {get; set;}

public Person()
{
Name = "Olaf";
}
}

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo blackdynamic,

die Veränderung an dem Objekt nicht mit.

implementiere das INotifyPropertyChanged-Interface und feuere das PropertyChanged-Event.

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!"

B
blackdynamic Themenstarter:in
51 Beiträge seit 2010
vor 12 Jahren

Ich habe die Person Klasse nun wie folgt angepasst:


public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return this._name; }

set
{
if (value != this._name)
{
this._name = value;
NotifyPropertyChanged("Name");
}
}
}

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}

public Person()
{
Name = "Olaf";
}
}

Es funktioniert allerdings leider trotzdem noch nicht.
Beim debuggen fällt auf, dass diese Bedingung "PropertyChanged != null" immer false ist und somit das Event nicht geschmissen wird.
Das diese Bedingung false ist sagt ja aus, dass es keine Abonnenten für das Event gibt, wisst ihr wo mein Fehler liegt?

T
574 Beiträge seit 2008
vor 12 Jahren

naja, hast du denn den Event auch irgendwo registriert? Also fängst du irgendwo das "PropertyChanged" auf?

B
blackdynamic Themenstarter:in
51 Beiträge seit 2010
vor 12 Jahren

Nein, weil ich nicht wirklich weiss wo ich es fangen sollte um mein Problem zu lösen. Ich will ja bloß, dass die Person aus der Liste innerhalb der Liste aktualisiert wird wenn eine Eigenschaft der Person in einer asynchronen Methode geändert wird.

T
574 Beiträge seit 2008
vor 12 Jahren

Von was für einer "Liste" reden wir da? Irgendeine ListView/ItemsControl o.ä. im WPF-Bereich oder WinForms, Web oder sonst was?

B
blackdynamic Themenstarter:in
51 Beiträge seit 2010
vor 12 Jahren

Die Person befindet sich in einer ObservableCollection

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo blackdynamic,

du hast nur einen Ausschnitt des Codes gepostet (was grundsätzlich auch ok ist). Daran lässt sich nicht erkennen, ob oder wann die Zuweisung des neuen Names ausgeführt wird. Und auch nicht, ob die Zuweisung an das gewollte Personen-Objekt erfolgt oder vielleicht ein anderes. Hast du (im Debugger) mal geschaut, ob die Zeile überhaupt ausgeführt wird?

herbivore

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo blackdynamic,

dass die Person aus der Liste innerhalb der Liste aktualisiert wird wenn eine Eigenschaft der Person in einer asynchronen Methode geändert wird.

Das passiert ja so auch, weil es dieselbe Referenz ist. Anders ist das bei structs od. immutablen Typen (wie String).
Es ist also egal ob ein anderen Thread als der UI-Threads das ändert. Wenn du aber wissen willst wann die Änderung stattfindet bzw. über die Änderung informiert werden willst -> INotifyPropertyChanged.

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!"

B
blackdynamic Themenstarter:in
51 Beiträge seit 2010
vor 12 Jahren

Vielen Dank für eure Geduld und Eure Hilfe!

Ich war auch der Meinung das die Referenz übergeben wird und genau das war auch der Grund warum ich so ratlos war.

Letztendlich lag das Problem nun darin, dass die Peson in der Liste zwar aktualisiert wurde (was ich auf deine Anmerkung hin direkt im Callback getestet habe) aber ich in meinem Unit Test noch die alte Liste hatte weil die Überprüfung des Tests nicht gewartet hat, bis der Callback fertig gelaufen war.

Das bedeutet der Fehler lag in meinem Unit Test, nochmals vielen Dank 😃

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo blackdynamic,

n meinem Unit Test noch die alte Liste hatte weil die Überprüfung des Tests nicht gewartet hat

Dazu gibts ab .net 4.0 einen netten Trick wie gewartet werden kann.


List<T> tmp = sut.MyListT;
SpinWait.SpinUntil(() => sut.MyListT != tmp, 1000);

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!"

B
blackdynamic Themenstarter:in
51 Beiträge seit 2010
vor 12 Jahren

Genial, damit läuft der Test durch! Danke 😃

T
574 Beiträge seit 2008
vor 12 Jahren

Da erschließt sich wieder mein alt bekanntes Problem: Der Entwickler (Lösungsgeber) kann das Problem erst erkennen, wenn der Fehlermelder auch alle Angaben gibt.
Meist ist es dann so, dass der Fehlermelder, wenn er dann die Angaben alle korrekt gesammelt hat, schon selber draufkommt was nicht stimmt und das Problem selbst behebt.

@blackdynamic
Hättest du von Anfang an erwähnt, dass du die Liste in einem Unit-Test überprüfst hätten wir uns wohl einige Beiträge (und Verwirrung) erspart ... 😉

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo tkrasinger,

es liegt in der Natur der Sache, dass ein Hilfesuchender, solange er nicht weiß, wo der Fehler liegt, auch nicht weiß, welche Hinweise zum Auffinden des Fehler wichtig sind - abgesehen von so offensichtlichen Punkten wie der exakten Fehlermeldung ... und selbst die wird oft genug unterschlagen. Wobei ich auch meine zu wissen warum: Denn wenn jemand den Fehler mit Hilfe der Fehlermeldung nicht selbst lösen kann, dann neigt er natürlich dazu, die Fehlermeldung als das einzuschätzen, was sie für ihn ist: Eine Information, die nicht weiterhilft. Und so ist das mit allen Informationen, die dem Hilfesuchenden vorliegen. Sie alle haben nicht geholfen, den Fehler zu finden, also werden oft unterschlagen.

Nur ist es keine Alternative, nun von Anfang an jedes noch so kleine Detail zu beschreiben und damit die Helfer mit Informationen zu fluten, denn viele Details sind zum Auffinden des Fehlers tatsächlich unnötig. Die exakte Fehlermeldung sollte natürlich trotzdem immer genannt werden.

Sei es wie es will. An dem Punkt, dass der Hilfesuchende, wenn er alle Informationen, die ihm zur Verfügung stehen oder die er leicht ermitteln kann, korrekt gesammelt und bewertet hätte, das Problem auch selber hätte lösen können, stimmen wir vollkommen überein. Das wird auch tatsächlich passieren. Nur wissen wir nicht wie oft - weil in diesem Fall ja kein Thread erstellt wird. 😃

herbivore