[英]How to register an event call a delegated method in a different class using an extension method in .NET Core?
我正在嘗試在不同類的實例之間實現異步數據移動,而不實際使用類引用並為其基類構建擴展方法。
它是一個.NET核心類庫,面向.NET Standard 1.6。
假設我有一個帶有async void
方法的類,它不斷更新同一個類的屬性:
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();
}
}
}
我還有一個帶有async void
方法的第二個類,它將數據寫入某處:
public abstract class DataWriter : DataWriterAbstract
{
public async void WriteData(float dataToWrite)
{
// some writing magic
}
}
如何構建一個擴展方法,基本上將兩個或多個這些類的實例“配對”並委托事件?
像這樣的東西:
public static DataRetrieverAbstract PairToWriter (this DataRetrieverAbstract retriever, DataWriterAbstract writer)
{
// ???
}
然后像這樣使用它:
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);
所以基本上我們有一個Retriever
只使用第二個實例DataWriter
和3-rd Retriever
兩個不同實例。
最好的方法是什么?
async void
東西有點像紅色鯡魚,但你可以使用一個有多個訂閱者的事件來實現這一點。 在內部,這類似於將閱讀器的實例存儲在編寫器內的列表中。 這是一個例子:
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();
}
}
您只需要向基本抽象類添加一些事件並訂閱它。 順便說一句,我建議使用Task而不是void,因為你的應用程序完成時你的讀/寫方法可能沒有完成,所以你不“看到”這些方法的結果:
在這里,它是DataRetrieverAbstract
和他的成熟類:
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);
}
}
接下來,如果要使用未派生的基本抽象類訂閱事件,則應將WriteData
移動到基類:
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);
}
}
所以你的擴展非常簡單:
public static DataRetrieverAbstract SubscribeOnReaded(this DataRetrieverAbstract retriever, DataWriterAbstract writer)
{
retriever.DataReaded += writer.WriteData;
return retriever;
}
用法:
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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.