Adding a method call to a collection - c#

Given a situation where I have a method SetFooInDevice(), which I call using a property as one of the arguments:
public class Program
{
public static byte Foo { get; set; }
public static void SetFooInDevice(System.IO.Ports.SerialPort sp, byte foo)
{
var txBuffer = new List<byte>();
// Format message according to communication protocol
txBuffer.Add(foo);
sp.Write(txBuffer.ToArray(), 0, txBuffer.Count);
}
public static void Main()
{
var _rnd = new Random();
var _serialPort = new System.IO.Ports.SerialPort("COM1", 9600);
_serialPort.Open();
for (int i = 0; i < 100; i++)
{
Foo = (byte)_rnd.Next(0, 255);
SetFooInDevice(_serialPort, Foo); // <-- How to add this call to a collection?
System.Threading.Thread.Sleep(100);
}
}
}
Is it possible to add the method call to a collection, in a way that the method call can be executed when running through the collection at a later time?
I want to be able to add calls to various methods to a collection, that I can run through and execute later if conditions are met (serial port open, time interval has passed, etc.).

Try this:
public static byte Foo { get; set; }
public static void SetFooInDevice(System.IO.Ports.SerialPort sp, byte foo)
{
var txBuffer = new List<byte>();
// Format message according to communication protocol
txBuffer.Add(foo);
sp.Write(txBuffer.ToArray(), 0, txBuffer.Count);
}
public static void Main()
{
List<Action> listActions = new List<Action>(); // here you create list of action you need to execute later
var _rnd = new Random();
var _serialPort = new System.IO.Ports.SerialPort("COM1", 9600);
_serialPort.Open();
for (int i = 0; i < 100; i++)
{
Foo = (byte)_rnd.Next(0, 255);
var tmpFoo = Foo; // wee need to create local variable, this is important
listActions.Add(() => SetFooInDevice(_serialPort, tmpFoo));
System.Threading.Thread.Sleep(100);
}
foreach (var item in listActions)
{
item(); // here you can execute action you added to collection
}
}
You can check this on MS docs Using Variance for Func and Action Generic Delegates

You can use Action delegate for this purpose like below
private List<Action<System.IO.Ports.SerialPort, byte>> methodCalls
= new List<Action<System.IO.Ports.SerialPort, byte>>();

Related

Start a Thread from different class and parameters

I would like to start the Thread with methods implemented in a external class and as well as I need to pass to this method reference to some external property.
The problem is here
Thread t = new Thread(Agent.Activate(agentParameters, ref tcpListener));
Visual Studio say that Acvivate method should return Thread. But by example below it should not.
I use this example but it does not help https://msdn.microsoft.com/en-us/library/system.threading.thread.setdata(v=vs.110).aspx. Here is my code
class TCPListenerManager
{
TcpListener tcpListener;
public TCPListenerManager(HostListenerItem hostListenerItem)
{
tcpListener = new TcpListener(IPAddress.Parse(hostListenerItem.IP4), hostListenerItem.Port);
for (int i = 0; i < hostListenerItem.ClientsMax; i++)
{
var agentParameters = new AgentParameters();
Thread t = new Thread(Agent.Activate(agentParameters, ref tcpListener));
t.Start();
}
}
} // end of class DeviceAgent
class Agent
{
[ThreadStaticAttribute]
static int threadSpecificData;
static public AgentParameters Parameters;
public static void Activate(AgentParameters agentParameters, ref TcpListener tcpListener)
{
Parameters = agentParameters;
threadSpecificData = Thread.CurrentThread.ManagedThreadId;
var socket = tcpListener.AcceptSocket();
if (socket.Connected)
{
//
// logger.Info("Socket.Connected on" + socketParameters.HostListenerItem.Name + " " + socketParameters.HostListenerItem.Url);
}
}
} // end of Agent
class AgentParameters
{
public HostListenerItem HostListenerItem { get; set; }
public AgentParameters()
{
HostListenerItem = new HostListenerItem();
}
}
You have to pass your method as a delegate to the new thread:
Thread t = new Thread(() => Agent.Activate(agentParameters, ref tcpListener));

Generate list with random numbers. Sort and total sum

I am new to programming and i would be delighted if someone could help me with the following problem.
I have problem with two methods.
First sum all of 50 random numbers in a list, second sort list with 50 random numbers.
I know how to do this when a list has for example four numbers{1,2,3,8} , but not with 50 random numbers.
It will be nice to use a constructor to this two methods, but I don't now how to do this.
Any help would be much appreciated.
class Class1
{
var rand = new Random();
public Class1( ) // how to use a constructor ?
public static List<double> TotalSum()
{
var alist = new List<double>();
for (int i = 0; i < 50; i++)
{
alist.Add(rand.Next(10));
}
Console.WriteLine("Total sum:", alist);
}
public static List<double> Sort()
{
var slist = new List<double>();
for (int i = 0; i < 50; i++)
{
slist.Sort(rand.Next(10)); // rand.Next does not work with Sort
Console.WriteLine(slist);
}
}
class Program
{
static void Main(string[] args)
{
Class1 show = new Class1();
show.TotalSum();
show.Sort();
}
}
}
just use the 2 methods
list.Sum();
list.Sort();
I suggest that you use rand.Next(Environment.TickCount) to get better random numbers, but at the end you need to use a different primitive value to store the result
class Program
{
static void Main(string[] args)
{
var array = Class1.Sort();
PrintArray(array);
Console.WriteLine();
Console.WriteLine("Total sum: {0}", Class1.TotalSum());
Console.ReadLine();
}
static void PrintArray(List<double> array)
{
foreach (var a in array)
Console.WriteLine(a);
}
}
public class Class1
{
public static double TotalSum()
{
var rand = new Random();
var alist = new List<double>();
for (int i = 0; i < 50; i++)
{
alist.Add(rand.Next(10));
}
return alist.Sum();
}
public static List<double> Sort()
{
var rand = new Random();
var slist = new List<double>();
for (int i = 0; i < 50; i++)
{
slist.Add(rand.Next(10)); // rand.Next does not work with Sort
}
slist.Sort();
return slist;
}
}
I think I got what you mean. You want a class that holds random list of numbers and you need some functions.
Its better to give more suitable name to your class. for example "RandomList" would be good.
You can't use Console.WriteLine(list) to print items of list. You have to iterate through items and print them in the way you want.
You can't use var to define fields. You have to write the type explicitly.
static keyword in other word is one for all instances. So if you dont use static it would be one per instance. Instance is just the object that you create with the constructor. Its better to declare random number generator as static, but other methods and list field should remain non static.
With the use of Linq Sum() you can get the sum of all the items from list. If you dont want to use Linq then you have to iterate through list and add each item to value sum.
I commented the code to explain things.
class Program
{
static void Main(string[] args)
{
RandomList show = new RandomList(50, 10);
show.TotalSum();
show.Sort();
}
}
class RandomList
{
private static Random rand = new Random();
private List<double> _list; // this field holds your list
public RandomList(int length , int maxValuePerItem) // this is the constructor
{
_list = new List<double>();
// populate list
for (int i = 0; i < length; i++)
{
_list.Add(rand.Next(maxValuePerItem));
}
}
public void TotalSum()
{
Console.WriteLine("Total sum: {0}", _list.Sum()); // {0} is required to specify the place to put _list.Sum() inside string.
// without linq way
// int sum = 0;
// foreach(var i in _list) sum += i;
}
public void Sort()
{
_list.Sort();
foreach (var d in _list)
{
Console.Write(d + " , "); // you need to print this in the way you want.
}
}
}
A constructor is typically used to initialize some values. In your case, you should use it to initialize your list, that is, to load it with 50 random numbers. We will also change it so that the list is a property of the class.
Note that the methods shouldn't be static, since they are acting on a property of the class. We are also using the built-in methods of List (Sum() and Sort()) instead of rolling our own.
class Class1
{
var rand = new Random();
var alist;
public Class1() // constructor creates a new list and initializes it
{
alist = new List<double>();
for (int i = 0; i < 50; i++)
{
alist.Add(rand.Next(10));
}
}
public List<double> TotalSum()
{
Console.WriteLine("Total sum:", alist.Sum());
}
public List<double> Sort()
{
alist.Sort();
for (double num in alist)
{
Console.WriteLine(num.ToString());
}
}
class Program
{
static void Main(string[] args)
{
Class1 show = new Class1();
show.TotalSum();
show.Sort();
}
}
}
Because you asked how to use the constructor I tried to rewrite your code in a way that the constructor do something of usefull. I added a property and a local variable plus another function.
public class Class1
{
Random rand = new Random();
List<double> alist { get; set; }
public Class1(int howmany = 50)
{
alist = new List<double>();
for (var i = 0; i < howmany; i++)
{
alist.Add(rand.NextDouble());
}
}
public void Sort()
{
alist.Sort();
}
public void printTotalSum()
{
Console.WriteLine("Total sum: {0}", alist.Sum());
}
public void printList() {
Console.WriteLine("The list contains:");
for (int i = 0; i < alist.Count; i++)
{
Console.WriteLine(alist[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
Class1 show = new Class1(10);
show.printTotalSum();
show.Sort();
show.printList();
}
}

Create instance of unknown type

I have class that draw three types of charts, and i want to update it by function public void GetData(PlotModel PlotModel). The main problem is that every series (AreaSeries,CandleStickSeries,HighLowSeries) has different interfaces. How can i update different types in function public void GetData(PlotModel PlotModel). What should i use Activator? Generic?
I think that something like is bad idea:
public void GetData(PlotModel PlotModel) {
while(true) {
System.Threading.Thread.Sleep(1000);
// Add new Item?
switch(PlotModel.Series.First().ToString()) {
case "OxyPlot.Series.AreaSeries":
Console.WriteLine("AreaSeries");
break;
case "OxyPlot.Series.CandleStickSeries":
Console.WriteLine("CandleStickSeries");
break;
case "OxyPlot.Series.HighLowSeries":
Console.WriteLine("HighLowSeries");
break;
}
}
}
Code:
namespace WpfApplication20 {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = new PlotClass();
}
}
public class PlotClass {
public PlotModel PlotModel { get; set; }
public PlotClass() {
PlotModel = new PlotModel();
DrawCandleChart(PlotModel);
UpdateChartAsync(PlotModel);
}
public void DrawSimpleChart(PlotModel PlotModel) {
Random rnd = new Random();
LineSeries LS = new LineSeries();
for (int i=0;i<10;i++) {
LS.Points.Add(new DataPoint(i,rnd.NextDouble()));
}
PlotModel.Series.Add(LS);
PlotModel.InvalidatePlot(false);
}
public void DrawCandleChart(PlotModel PlotModel) {
Random rnd = new Random();
CandleStickSeries CSS = new CandleStickSeries();
for (int i=0;i<10;i++) {
CSS.Items.Add(new HighLowItem { Close = rnd.NextDouble(), High = rnd.NextDouble(), Low = rnd.NextDouble(), Open = rnd.NextDouble(), X = i });
}
PlotModel.Series.Add(CSS);
PlotModel.InvalidatePlot(false);
}
public void DrawHighLowChart(PlotModel PlotModel) {
Random rnd = new Random();
HighLowSeries HLS = new HighLowSeries();
for (int i = 0; i < 10; i++) {
HLS.Items.Add(new HighLowItem { Close = rnd.NextDouble(), High = rnd.NextDouble(), Low = rnd.NextDouble(), Open = rnd.NextDouble(), X = i });
}
PlotModel.Series.Add(HLS);
PlotModel.InvalidatePlot(false);
}
public void UpdateChartAsync(PlotModel PlotModel) {
Action<PlotModel> Update = new Action<PlotModel>(GetData);
IAsyncResult result = Update.BeginInvoke(PlotModel, null, null);
}
public void GetData(PlotModel PlotModel) {
while(true) {
System.Threading.Thread.Sleep(1000);
// Add new Item?
}
}
}
}
C# 4 and up offers a nice way of processing situations like this: use cast to dynamic, and call a method with one overload per subtype, like this:
private void Process(AreaSeries arSer) {
...
}
private void Process(CandleStickSeries csSer) {
...
}
private void Process(HighLowSeries hlSer) {
...
}
...
while(true) {
System.Threading.Thread.Sleep(1000);
Process((dynamic)PlotModel.Series.First());
// ^^^^^^^^^
}
The cast to dynamic makes the "magic" happen: C# will examine the run-time type of PlotModel.Series.First(), and dispatch to one of the three Process methods that you supplied.
There is a danger in this approach: if PlotModel.Series.First() happens to not match any of the overloads, you get a run-time exception. The compiler cannot perform static analysis to tell you that your call would not succeed. Consider adding a catch-all method for the common superclass of your plots, too, so that you could handle unexpected sub-types more gracefully.

Passing a parameter to a thread

I have a problem using threads. There is a class like this:
public class MyThread
{
public void Thread1(int a)
{
for (int i = 0; i < 1000000; i++)
{
j++;
for (int i1 = 0; i1 < 1000; i1++)
{
j++;
}
}
MessageBox.Show("Done From Class");
}
}
and I use this below code for using it:
private void button1_Click(object sender, EventArgs e)
{
MyThread thr = new MyThread();
Thread tid1 = new Thread(new ThreadStart(thr.Thread1));
tid1.Start();
MessageBox.Show("Done");
}
I get error because of Thread1 Parameter (int a),
there isn't any problem when I haven't got any parameter.
How can I fix it?
A preferred method is the first one as you can pass multiple parameters to your method without having to cast to object all the time.
Thread t= new Thread(() => thr.Thread1(yourparameter));
t.Start();
Alternatively, you need to use parameterised thread as you are passing parameter to thread. you can also do
Thread t = new Thread (new ParameterizedThreadStart(thr.Thread1));
t.Start (yourparameter);
ofcourse your parameter has to be of object type for second example.
Threads accept a single object parameter:
public void Thread1(object a1)
{
int a = (int)a1;
...
}
Pass it like this:
Thread t = new Thread(Thread1);
t.Start(100);
You don't normally need to build delegates. Doing new ThreadStart(...) is normally useless from C# 2.0 .
Another (common) solution is to put Thread1 in another object:
public class MyThread
{
public int A;
public void Thread1()
{
// you can use this.A from here
}
}
var myt = new MyThread();
myt.A = 100;
var t = new Thread(myt.Thread1)
t.Start();
This because delegates have a reference to the containing object of the method. Clearly in this way you lose access to the caller's object... But then you could do:
public class MyThread
{
public int A;
public CallerType ParentThis;
public void Thread1()
{
// you can use this.A from here
// You can use ParentThis.Something to access the caller
}
}
var myt = new MyThread();
myt.A = 100;
myt.ParentThis = this;
var t = new Thread(myt.Thread1)
t.Start();
A final common method is to use closures, as suggested by Ehsan Ullah (the example with the () =>)

Two parameters to a C# thread

I can send one parameter in the thread
class myThread
{
Thread thread;
public myThread(string name, int st)
{
thread = new Thread(this.Get_IP);
thread.Name = name;
thread.Start(st);//передача параметра в поток
thread.Join();
}
void Get_IP(object st)
{
for (int ii = 0; ii < (int)st; ii++)
{
// smth
}
}
}
But i need to send two of them
for example
for (int ii = (int)st; ii < (int)fi; ii++)
{
// smth
}
there is a way to put 2 params all together
void A(int a, int b) { }
and
ThreadStart starter = delegate { A(0, 10); };
But how can i send them to the thread?
You can pass more parameters to thread by using lambda expresion. Like this:
Thread thread = new Thread(()=>A(5,6));
Put the two variables as members in the class:
class MyThread {
private Thread _thread;
private int _start, _finish;
public MyThread(string name, int start, int finish) {
_start = start;
_finish = finish;
_thread = new Thread(Get_IP);
_thread.Name = name;
_thread.Start();
_thread.Join();
}
void Get_IP() {
for (int ii = _start; ii < _finish; ii++) {
// smth
}
}
}
Note: Calling Join right after starting the thread makes is pretty pointless to use a thread.
The Thread.Start method accepts an object as parameter. You can pass an array of your values.
thread.Start(new object[] { a, b });
Create a class that hold all of the values you need to pass and pass an instance of that class to your thread.
If you need to send 2 parametes, you can send them as any type you like, but in the method that starts new thread, you have to unbox it/them:
void MyMethod()
{
int a = 1;
int b = 2;
int[] data = new[] { a, b };
Thread t = new Thread(new ParameterizedThreadStart(StartThread));
t.Start(data);
}
private void StartThread(object obj)
{
int[] data = obj as int[];
if (data != null)
{
int a = data[0];
int b = data[1];
}
}
NOTE: method that is called by new Thread can only accppt object parameter. What is inside this object is not code`s concern, can be anything, like I boxes 2 integers.
Then you simply unbox the object to your original data types.

Categories

Resources