I'm trying to achieve async data movement between instances of different classes without actually using class references and building an extension method for their base classes instead.
It's a .NET Core Class Library, targeting .NET Standard 1.6.
Let's say I have a class with an async void
method that continuously updates a property of the same class:
public abstract class DataRetriever : DataRetrieverAbstract
{
public int CollectionInterval { get; private set; }
public float DataResult { get; private set; }
private float ReadData()
{
return 1; //in reality it returns different values every time
}
public async void StartReading(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(CollectionInterval * 1000);
DataResult = ReadData();
}
}
}
I also have a second class with an async void
method which writes the data somewhere:
public abstract class DataWriter : DataWriterAbstract
{
public async void WriteData(float dataToWrite)
{
// some writing magic
}
}
How can I build an extension method that basically is going to "pair" two or more instances of these classes together and delegate the events?
Something like this:
public static DataRetrieverAbstract PairToWriter (this DataRetrieverAbstract retriever, DataWriterAbstract writer)
{
// ???
}
To then use it like this:
var dataRetriever1 = new DataRetriever();
var dataRetriever2 = new DataRetriever();
var dataRetriever3 = new DataRetriever();
var dataWriter1 = new DataWriter();
var dataWriter2 = new DataWriter();
dataRetriever1.PairToWriter(dataWriter1).PairToWriter(dataWriter2);
dataRetriever3.PairToWriter(dataWriter2);
// ... stuff goes on
dataRetriever1.StartReading(token);
dataRetriever2.StartReading(token);
dataRetriever3.StartReading(token);
So basically we have one Retriever
writing into two different instances of DataWriter
and 3-rd Retriever
using just the second instance.
What is the best way to do it?
The async void
stuff is a bit of a red herring, but you could achieve this using an event with multiple subscribers. Internally, this is similar to storing instances of the reader in a list inside the writer. Here's an example:
class Program
{
class DataRetriever
{
public event Action<float> DataReady;
private float ReadData() => 1;
public async Task StartReading()
{
while (true)
{
await Task.Delay(1000);
DataReady?.Invoke(ReadData());
}
}
}
class DataWriter
{
public void WriteData(float dataToWrite)
{
Console.WriteLine(dataToWrite);
}
}
static void Main(string[] args)
{
var reader1 = new DataRetriever();
var reader2 = new DataRetriever();
var reader3 = new DataRetriever();
var writer1 = new DataWriter();
var writer2 = new DataWriter();
reader1.DataReady += writer1.WriteData;
reader2.DataReady += writer2.WriteData;
reader3.DataReady += writer2.WriteData;
Task.Run(reader1.StartReading);
Task.Run(reader2.StartReading);
Task.Run(reader3.StartReading);
Console.ReadKey();
}
}
You just need to add some event to base abstract class and subscribe to it. By the way, I suggest to use Task instead of void, because your read/write method can be non completed when your app is finished, so you doesn't "see" the result of these methods:
Here it's DataRetrieverAbstract
and his derrived class:
public abstract class DataRetrieverAbstract
{
public virtual event Action<float> DataReaded;
protected void FireDataReaded(float arg)
{
DataReaded?.Invoke(arg);
}
}
public class DataRetriever : DataRetrieverAbstract
{
public int CollectionInterval { get; set; }
public float DataResult { get; private set; }
private float ReadData()
{
return 1; //in reality it returns different values every time
}
public async Task StartReading(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(CollectionInterval * 1000);
DataResult = ReadData();
}
FireDataReaded(DataResult);
}
}
Next you should move WriteData
to base class if you want to subscribe on event using base abstract class not derived:
public abstract class DataWriterAbstract
{
public abstract void WriteData(float dataToWrite);
}
public class DataWriter : DataWriterAbstract
{
public override void WriteData(float dataToWrite)
{
// some writing magic
Console.WriteLine(dataToWrite);
}
}
So your extension is very simple:
public static DataRetrieverAbstract SubscribeOnReaded(this DataRetrieverAbstract retriever, DataWriterAbstract writer)
{
retriever.DataReaded += writer.WriteData;
return retriever;
}
And usage:
var dataRetriever1 = new DataRetriever() { CollectionInterval = 2 };
var dataRetriever2 = new DataRetriever() { CollectionInterval = 3 };
var dataRetriever3 = new DataRetriever() { CollectionInterval = 4 };
var dataWriter1 = new DataWriter();
var dataWriter2 = new DataWriter();
dataRetriever1.SubscribeOnReaded(dataWriter1).SubscribeOnReaded(dataWriter2);
dataRetriever3.SubscribeOnReaded(dataWriter2);
//...
CancellationTokenSource source = new CancellationTokenSource();
var tasks = new[] { dataRetriever1.StartReading(source.Token), dataRetriever2.StartReading(source.Token), dataRetriever3.StartReading(source.Token) };
source.Cancel();
// If you want to wait a tasks results – uncomment the line below
//Task.WaitAll(tasks);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.