如何使用.NET Core中的扩展方法在不同的类中注册事件调用委托方法?

时间:2017-08-13 09:31:10

标签: c# .net .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的两个不同实例。

最好的方法是什么?

2 个答案:

答案 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);