我试图在不同类的实例之间实现异步数据移动,而不实际使用类引用并为其基类构建扩展方法。
它是.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
的两个不同实例。
最好的方法是什么?
答案 0 :(得分:4)
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();
}
}
答案 1 :(得分:3)
您只需要向基本抽象类添加一些事件并订阅它。顺便说一句,我建议使用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);