I'm having problems with passing loaded data from one thread to another to add the data to the form. I added another object into "var obj = new object[] { names }" and got "Parameter count mismatch". I'd still prefer to pass "Clients" and "Messages" from "LoadData()" in the second thread to "UpdateFormMethod()" in the first one, but I have no idea how to do it. I'd be grateful if anyone could help me with this problem.
Here's the important part of the code within one class:
private readonly Thread _thread;
public readonly Loader Loader = new Loader();
public Dictionary<string, Client> Clients;
public Dictionary<string, Message> Messages;
private bool _stopData = false;
public delegate void UpdateForm(object data);
public UpdateForm MyDelegate;
public Fcon() {
InitializeComponent();
MyDelegate = new UpdateForm(UpdateFormMethod);
_thread = new Thread(LoadData);
_thread.Start();
}
public void UpdateFormMethod(object data) {
foreach (var str in ((IEnumerable<string>)data).Where(str => !fcon_container_users_list.Items.Contains(str))) {
fcon_container_users_list.Items.Insert(0, str);
}
}
public void LoadData() {
while (!_stopData) {
Clients = Loader.GetClients(Operator);
Messages = Loader.GetMessages(Operator);
var status = Loader.SetStatus(Operator);
var names = new string[Clients.Count];
var x = 0;
foreach (var kvp in Clients) {
names[x] = "user_" + kvp.Value.id_fcon_client;
x++;
}
var obj = new object[] { names };
this.Invoke(this.MyDelegate, obj);
Thread.Sleep(1000);
}
}
public void StopData() {
_stopData = true;
}
It sounds like you're not entirely clear where the signature is coming from. It's your own delegate - if you want to change the signature, just change the signature of the delegate and the method implementing it:
public delegate void UpdateForm(IEnumerable<string> data, int foo);
...
public void UpdateFormMethod(IEnumerable<string> data, int foo) {
foreach (var str in data.Where(str =>
!fcon_container_users_list.Items.Contains(str))) {
fcon_container_users_list.Items.Insert(0, str);
}
}
Then:
var obj = new object[] { names, someIntVariable };
this.Invoke(this.MyDelegate, obj);
Or more simply:
this.Invoke(this.MyDelegate, names, someIntVariable);
I'd probably use the existing Action<T>, Action<T1, T2> delegates rather than creating new ones though OR T Func<T1 [,T2]> if you require a return type.
you are passing an object [] in but you should only pass in object.
To send it in just cast it as object i.e.
this.Invoke(this.MyDelegate, (object)obj);
and cast it back inside the method. The reason they specified object as the parameter type is that it allows them not to have to specify many many signatures I think
Related
I have a custom class from which I create over 200 objects for a Gui.
In my main class with my business logic I want to apply the instantiation of all my objects, attach them to an event handler and set their Name. Instead of doing all this by hand for every object I thought to create a method that takes as parameters a "params" list of my objects. The problem is that this method seems to work out a "copy" of my objects instead the reference of those. What I have so far is:
My object base class:
public class MyObject
{
... // this works
}
My business class now (what works):
public class Logic
{
public MyObject Object001, Object002,... Object200; // note that they are not instantiated yet
public Logic()
{
Object001 = new MyObject();
Object001.Name = nameof(Object001);
Object001.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
Object002 = new MyObject();
Object002.Name = nameof(Object002);
Object002.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
...
Object200 = new MyObject();
Object200.Name = nameof(Object200 );
Object200.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
}
My desired business class (what has not worked):
public class Logic
{
public MyObject Object001, Object002,... Object200; // note that they are not instantiated yet
public Logic()
{
InstantiateAllObjects(Object001, Object002, ..., Object200); // here my 200 objects!
}
private void InstantiateAllObjects(params MyObject[] list)
{
for (int i=0; i<list.Length; i++)
{
// if(list[i]==Object001) Console.WriteLine("Object001== null?: " + (Object001 == null)); --> this executes for EVERY object, instead for only Object001!!
MyObject obj = list[i];
obj = new MyObject();
obj.Name = nameof(obj); // why "nameof(list[i])" didn't work?
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
// if (list[i] == Object001) Console.WriteLine("Object001== null?: " + (Object001== null)); --> this executes again for EVERY object AND (Object001== null) is ALWAYS true!!
}
}
}
Can anybody explain me why my method seems not to create my objects?
Thanks in advance for your time and help!
EDIT
In my opinion, the problem seems to be i have to pass a reference to these declared objects in the 'params' list of my method... is it possible? how? I tried to use the modifiers "out" and "ref" near the "params" in the method, but with "params" seems not to be possible...
EDIT2
Following some suggestions I created in my class: logic an object list:
MyObject[] list = new MyObject[] { Object001, Object002, ..., Object200 };
InstantiateAllObjects(ref list);
and modified my method as per private void InstantiateAllObjects(ref MyObject[] list) iterating inside over list[i], but unfortunately with the same wrong result...
and also tried
List<MyObject> list = new List<MyObject>() { Object001, Object002, ..., Object200 };
InstantiateAllObjects(ref list);
and modified my method as per private void InstantiateAllObjects(ref List<MyObject> list) iterating inside over list[i], but unfortunately also with the same wrong result...
Forget about all those Object001, Object002 etc fields. Just use a new List<MyObject>() and add your objects into it inside the for-loop in your InstantiateAllObjects.
public class Logic
{
private readonly List<MyObject> allMyObjects; // note that they still are not instantiated yet
public Logic()
{
cont int amount = 200;
allMyObjects = new List<MyObject>(amount); // reserve space, but all are still null
InstantiateAllObjects(allMyObjects, amount);
}
private void InstantiateAllObjects(List<MyObject> list, int amount)
{
for (int i=0; i<amount; i++)
{
MyObject obj = new MyObject();
obj.Name = "Object" + (i+1).ToString("000");
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
list.Add(obj); // place the newly created object in the list
}
}
}
Your original test if(list[i]==Object001) fires every time, because both list[i] (for every i) and Object001 are null.
Also note that you can have multiple references to one instance of your MyObject (this already happens when you pass the reference as parameter to some method). The fact that one of those is called "Object001" is not important and in fact unknown to the instance. That is why nameof(list[i]) cannot return "Object001".
Another try, based on Hans Kesting'a answer and anna's statement
2) the name of every object will be (very) different one to another,
it is here just that i used the indexes 001...200 to concept the idea,
To avoid any trouble with accessing the fields and to regard what you wrote in the comments, no getter/setter is used and everything is public. Not the best approach but well, compared to handling 200 separate objects...
public class Logic
{
public List<MyObject> MyObjectList;
public List<string> MyObjectNames;
public Logic()
{
var anotherClass = new AnotherClass();
MyObjectNames = new List<string>() {"Object01", "Object02", "Object03"}; // either add your names here...
MyObjectNames.Add("Object04"); // or add additional names this way
//MyObjectNames.AddRange(anotherNameList); // or add another list or use Linq or whatever
MyObjectList = anotherClass.InstantiateAllObjects(MyObjectNames);
}
}
public class AnotherClass
{
public List<MyObject> InstantiateAllObjects(List<string> nameList)
{
var objectList = new List<MyObject>(nameList.Count);
foreach (var name in nameList)
{
objectList.Add(new MyObject(){Name = name});
}
return objectList;
}
}
Does this meet your requirements?
If you prefer a Dictionary, it's similar:
public class Logic
{
public Dictionary<string, MyObject> MyObjectDict;
public List<string> MyObjectNames;
public Logic()
{
var anotherClass = new AnotherClass();
MyObjectNames = new List<string>() { "Object01", "Object02", "Object03" }; // either add your names here...
MyObjectNames.Add("Object04"); // or add additional names this way
//MyObjectNames.AddRange(anotherNameList); // or add another list or use Linq or whatever
MyObjectDict = anotherClass.InstantiateAllObjects(MyObjectNames);
// objects in dict can be accessed directly by their names:
var object01 = MyObjectDict["Object01"];
}
}
// You can access in derived classes or any other classes
public class DerivedLogic : Logic
{
public void SomeFunc()
{
var object01 = MyObjectDict["Object01"];
}
public void SomeOtherFunc(string objectName)
{
var object01 = MyObjectDict[objectName];
}
}
public class AnotherClass
{
public Dictionary<string, MyObject> InstantiateAllObjects(List<string> nameList)
{
var objectList = new Dictionary<string, MyObject>(nameList.Count);
foreach (var name in nameList)
{
// check if object with name does not already exist.
if(!objectList.ContainsKey(name)
{
// For your property changed assignment, you can separate the object creation and DIctionary/List assignment
var obj = new MyObject() { Name = name };
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
objectList.Add(name, obj);
}
// else .... doe something
}
return objectList;
}
}
All of those arguments are being passed by value. You're passing the references, but the InstantiateAllObjects method can't change the values of any of the fields.
Simplifying this a bit, it's a little like having code like this:
string x = "Original value";
// This copies the value of x into the array
string[] array = new string[] { x };
// This changes the array element, but doesn't affect the x variable at all
array[0] = "Different value";
// Still prints "Original value", because x hasn't changed
Console.WriteLine(x);
As noted in comments, using a list is likely to be a much better approach than lots of different fields here.
Use a class (can be the same as the one with the logic) to hold your MyObject Fields, pass it into a function that uses reflection to populate the fields.
// Class with Fields
public class MyObjectCollection
{
public MyObject Object001;
public MyObject ObjTwo;
}
public class Logic
{
// Init logic
public void Initialise(object controls)
{
Type targType = typeof(MyObject);
var t = controls.GetType();
// iterate all fields of type MyObject
foreach(var fi in t.GetFields().Where(f=>f.FieldType == targType))
{
// initialise as required.
var o = new MyObject();
o.Name = fi.Name;
fi.SetValue(controls, o);
}
}
}
If the field name is not enough for the object name you could use Attributes on the fields to direct initialisation.
This should do the job. It is a reflection-based approach and what it does is:
find all public instance fields
use only those whose name contains Object
create a new instance of MyObject and store that in the field
public void InstantiateAllObjects()
{
foreach (FieldInfo field in this.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.Name.Contains("Object")))
{
MyObject obj= new MyObject();
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
field.SetValue(this, obj);
}
}
If you add above method to your Logic class, you can then call it without having to explicitly pass Object001, Object002 etc. to it.
public class Logic
{
public MyObject Object001, Object002, ... Object200;
public Logic()
{
this.InstantiateAllObjects();
}
}
EDIT: Differently named fields
If the fields do not share a common prefix (that is, if they are not all named Object + ...), there are other ways to get the fields:
.Where(x => x.FieldType == typeof(MyObject))
yields only the fields whose type is MyObject.
You could also create an attribute
[AttributeUsage(AttributeTargets.Field)]
public sealed class FieldIWantToSetAttribute : Attribute { }
and apply that to all fields you want to set, e.g.
public class Logic
{
[FieldIWantToSet] public MyObject Object001; // will be set
[FieldIWantToSet] public MyObject Foo; // will be set
public MyObject Bar; // will not be set
}
Then, change the Where to
.Where(x => x.GetCustomAttribute<FieldIWantToSet>() != null)
Please however note that 1. you should definitely use caching. Reflection without caching is expensive, and 2. please overthink your design - why exactly do you feel the need to expose 200 fields for every other class to see?
How do I iterate over the anonymous type that is passed in as an object below (first, second, third) => new { One = first, Two = second, Three = third }
If I interrogate the type of message and print it, it says:<>f__AnonymousType0 3[MtApi.MtQuote,MtApi.MtQuote,MtApi.MtQuote]
//**How do I convert an object to the anonymous type?**
static void ShowAnonymousTypeMessage(object message)
{
foreach(var quote in message)
Console.WriteLine(
quote.Instrument + ": " + quote.Bid.ToString() + quote.Ask.ToString());
}
...
var pattern = observable1.And(observable2).And(observable3);
var plan = pattern.Then((first, second, third) => new { One = first, Two = second, Three = third });
var zippedSequence = Observable.When(plan);
zippedSequence.Subscribe(
ShowAnonymousTypeMessage
);
This is working for me:
static void Main()
{
var anon = new { Name = "Terry", Age = 34 };
test(anon);
}
static void test(dynamic t)
{
Console.WriteLine(t.Age);
Console.WriteLine(t.Name);
}
Anonymous types aren't intended to be passed around and you should only use object when absolutely necessary. Also you can't iterate over an anonymous type - you should use an Array.
var pattern = observable1.And(observable2).And(observable3);
var plan = pattern.Then((first, second, third) => new[] { first, second, third });
var zippedSequence = Observable.When(plan);
zippedSequence.Subscribe(
ShowAnonymousTypeMessage
);
Anonymous types aren't meant to be passed around, for the same reason we have strong typing in C# at all: The compiler doesn't make careless errors or forget things, and we often do. If your anonymous class instances are leaving the scope where they were created, it's time for them to grow up and be a real class.
Usually I'd say you should write a quickie class with appropriate properties (guessing at the property types here):
public class Thing {
public String One { get; set; }
public String Two { get; set; }
public String Three { get; set; }
}
But a Tuple<T1,T2,T3> is just as good really, if you've got property names like One, Two, and Three anyway:
public static void Main()
{
var x = Enumerable.Range(0, 10).Select(n => new Tuple<int, string>(n, $"Item {n + 1}"));
Test(x);
}
private static void Test(IEnumerable<Tuple<int, string>> stuff)
{
foreach (var item in stuff)
{
Console.Write($"{item.Item1}: {item.Item2}");
}
}
dynamic works, but dynamic is like the Vise-Grip: "Always the Wrong Tool for Every Job, Since 1921". dynamic has a legitimate but small role in the typesystem. It's not there so we can turn the whole language into JavaScript.
public static Main()
{
var x = Enumerable.Range(0, 10).Select(n => new { ID = n, Value = $"Item {n + 1}" });
Test(x);
}
private static void Test(dynamic message)
{
foreach (dynamic item in message)
{
Console.Write($"{item.ID}: {item.Value}");
}
}
OK, the Vise-Grip isn't always the wrong tool either. But it's rare there isn't a better one.
I have a Hash-table that contain different objects. Now i want to call a function (StartEvaluation) on each object when i traverse through the Hash-table using for loop (all object have same function). i have try to cast object on run time and i was not able to. can someone explain me how to do it?
Hashtable EvaluationObjects = new Hashtable();
// Location Evaluation
LocationEvaluation le = new LocationEvaluation();
// Asset Evaluation
AssetEvaluation ae = new AssetEvaluation();
// Point Evaluation
PointEvaluation pe = new PointEvaluation();
EvaluationObjects.Add("LocationEvaluation", le);
EvaluationObjects.Add("AssetEvaluation", ae);
EvaluationObjects.Add("PointEvaluation", pe);
// calling function
void objectProcessTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e,string objectType,string message)
{
System.Timers.Timer tm = (System.Timers.Timer)sender;
tm.Stop();
var obj = EvaluationObjects[objectType];
var s = obj.GetType();
object obj =(object)EvaluationObjects[objectType];
//MethodInfo method = typeof(obj).GetMethod("GenericMethod");
//MethodInfo generic = method.MakeGenericMethod(myType);
//generic.Invoke(this, null);
}
You should create an interface (or base class), then have a Dictionary storing objects implementing that interface, like in this example:
using System.Collections.Generic;
interface IEvaluation {
void StartEvaluation();
}
class LocationEvaluation : IEvaluation {
public void StartEvaluation() {
// do something...
}
}
class AssetEvaluation : IEvaluation {
public void StartEvaluation() {
// do something...
}
}
class Program {
static void Main(string[] args) {
// fill dictionary with IEvaluation objects
Dictionary<string, IEvaluation> evaluations = new Dictionary<string, IEvaluation>();
evaluations["LocationEvaluation"] = new LocationEvaluation();
evaluations["AssetEvaluation"] = new AssetEvaluation();
// get an object from the dictionary and call the function on it
IEvaluation evaluation = evaluations["AssetEvaluation"];
evaluation.StartEvaluation();
}
}
Dearest fellow programmers,
I seem to lack some understanding as of how the referencing works in C#.
The case:
I tried to implement some sort of Memento proxy which would wrap an interface and store every parameter that we're provided to the method calls and store these into a list.
Whenever necessary we could call the RestoreState and the objects would "reset" to the original state.
The code:
Consumer and model object
class Program
{
static void Main(string[] args)
{
IMemento memento = new Memento();
PrestationInfo prestationInfo2 = new PrestationInfo { Advance = 2 };
memento.Add(prestationInfo2);
Console.WriteLine(prestationInfo2.Advance); //Expect 2
prestationInfo2.Advance = 1;
Console.WriteLine(prestationInfo2.Advance); //Expect 1
memento.RestoreState();
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Console.ReadKey();
}
}
[Serializable]
public class PrestationInfo
{
public int Advance { get; set; }
}
Memento
public interface IMemento
{
void Add(object pItem);
void RestoreState();
}
public class Memento : IMemento
{
public Memento()
{
MementoList = new Dictionary<long, object>();
ReferenceList = new List<object>();
ObjectIDGenerator = new ObjectIDGenerator();
}
private ObjectIDGenerator ObjectIDGenerator { get; set; }
private Dictionary<long, object> MementoList { get; set; }
private List<object> ReferenceList { get; set; }
public void Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
}
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
private static TCopy DeepCopy<TCopy>(TCopy pObjectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, pObjectToCopy);
memoryStream.Position = 0;
return (TCopy)binaryFormatter.Deserialize(memoryStream);
}
}
}
Extra info
My guess is, I'm doing/understand something wrong regarding the List.
I also tried the Interlocked.Exchange, playing around with "ref"'s, using WeakReference's and storing the object into a CareTaker object (and storing that CareTaker into the List), implement some copy Property thing...
And ... I just can't see it.
My expected result would be the PrestationInfo.Advance property containing the value 2. But it keeps
Try this:
Change the Add method:
public long Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
return id; // i need my memento! LOL
}
You should also add this accessor method:
public object GetRestoredState(long id)
{
return MementoList[id]; // you should put some range check here
}
Now that you have your id, you can fetch the restored state this way:
memento.RestoreState();
prestationInfo2 = memento.GetRestoredState(savedId); // <-- you got this when you called the Add()...
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Follow ups: you can also make the IMemento into a IMemento<T>, and adjust your code accordingly
Memento.Add needs a ref parameter modifier to access the original reference type pointer.
https://msdn.microsoft.com/en-us/library/14akc2c7.aspx?f=255&MSPPError=-2147217396
It looks like the problem is in your understanding of references in .NET
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
The RestoreState method above does not return anything, and you're strictly operating on references, not their internal state. Inside your method object reference is a local reference. It is not the same as the external prestationInfo2 and your method simply makes reference point (refer) to the previously saved copy of the state of presentationInfo2.
You could modify it something like this:
public object RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
return reference;
}
}
return null;
}
And then call it like this:
presentationInfo2 = memento.RestoreState();
If you want the memento to track objects and magically restore their state you will have to make the objects themselves aware of the memento which introduces coupling or use reflection to modify the tracked references internal state. Basically you don't deserialize the persisted state into a new object but use reflection to overwrite the previously stored internal state into the tracked object reference.
Be careful though to store references using WeakReference otherwise you will find yourself with a nice case of memory leak.
List<int> data=new List<int>();
foreach(int id in ids){
var myThread=new Thread(new ThreadStart(Work));
myThread.Start(id);
}
Work(){
}
Method Work does some processing on the received id and then adds the result to the data list? How can I add data to the collection from each thread? How would my code look like? thanks
If you're using .NET 4, I strongly suggest you use Parallel Extensions instead. For example:
var list = ids.AsParallel()
.Select(Work)
.ToList();
where Work is:
public int Work(int id)
{
...
}
so that it can receive the id appropriately. If you're not keen on the method conversion, you could add a lambda expression:
var list = ids.AsParallel()
.Select(id => Work(id))
.ToList();
Either way, this will avoid creating more threads than you really need, and deal with the thread safety side of things without you having to manage the locks yourself.
First of all, you need to protect your multithreaded access with a lock. Second, you need to pass the parameter to your thread (or use lambda which can capture the local variable; beware that if you capture loop variable, it will change the value during the loop, so you ought to have a local copy).
object collectionLock = new object();
List<int> data = new List<int>();
foreach (int id in ids)
{
Thread t = new Thread(Worker);
t.Start(id);
}
void Worker(object o)
{
int id = (int)o;
lock(collectionLock)
{
data.Add(id);
}
}
you can pass and retrieve data (using callbacks) from threads. See MSDN article.
Example:
public class SomeClass
{
public static List<int> data = new List<int>();
public static readonly object obj = new object();
public void SomeMethod(int[] ids)
{
foreach (int id in ids)
{
Work w = new Work();
w.Data = id;
w.callback = ResultCallback;
var myThread = new Thread(new ThreadStart(w.DoWork));
myThread.Start();
}
}
public static void ResultCallback(int d)
{
lock (obj)
{
data.Add(d);
}
}
}
public delegate void ExampleCallback(int data);
class Work
{
public int Data { get; set; }
public ExampleCallback callback;
public void DoWork()
{
Console.WriteLine("Instance thread procedure. Data={0}", Data);
if (callback != null)
callback(Data);
}
}