簡體   English   中英

如何使用.NET Core中的擴展方法在不同的類中注冊事件調用委托方法?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM