C# 在为同一功能实现同时具有同步和异步API的库时使用async await

C# 在为同一功能实现同时具有同步和异步API的库时使用async await,c#,.net,async-await,dry,C#,.net,Async Await,Dry,关于如何在库中提供相同功能的同步和异步实现,我有几个问题。我将首先询问他们,然后提供下面的示例代码(这实际上相当多,但实际上非常简单) 是否有避免违反干燥原则的方法?考虑 jsRooSurviv.Read < >代码> jsRooScript。编写< >代码> jsRooScript。FLASH , PrimoLoMeService。发送< /COD>, PrimoLoMeSung.Engule及其异步版本。 当对同一方法的同步和异步版本进行单元测试时,是否有一种方法可以避免违反DRY原则?

关于如何在库中提供相同功能的同步和异步实现,我有几个问题。我将首先询问他们,然后提供下面的示例代码(这实际上相当多,但实际上非常简单)

  • 是否有避免违反干燥原则的方法?考虑<代码> jsRooSurviv.Read < <代码> >代码> jsRooScript。编写< <代码> >代码> jsRooScript。FLASH ,<代码> PrimoLoMeService。发送< /COD>,<代码> PrimoLoMeSung.Engule<代码>及其异步版本。

  • 当对同一方法的同步和异步版本进行单元测试时,是否有一种方法可以避免违反DRY原则?我正在使用NUnit,尽管我想所有的框架在这方面应该是相同的

  • 考虑到
    Take 1
    Take 2
    变量
    ComplexClass.Send
    ComplexClass.Receive
    ,如何实现返回
    Task
    Task
    的方法?哪一个是正确的,为什么

  • 考虑到不知道库将在何处使用(控制台应用程序、Windows窗体、WPF、ASP.NET),在库中的
    等待
    之后始终包含
    .ConfigureAwait(false)
    是否正确

  • 下面是我在第一个问题中提到的代码

    IWriter
    JsonStreamWriter

    public interface IWriter
    {
        void Write(object obj);
        Task WriteAsync(object obj);
        void Flush();
        Task FlushAsync();
    }
    
    public class JsonStreamWriter : IWriter
    {
        private readonly Stream _stream;
    
        public JsonStreamWriter(Stream stream)
        {
            _stream = stream;
        }
    
        public void Write(object obj)
        {
            string json = JsonConvert.SerializeObject(obj);
            byte[] bytes = Encoding.UTF8.GetBytes(json);
            _stream.Write(bytes, 0, bytes.Length);
        }
    
        public async Task WriteAsync(object obj)
        {
            string json = JsonConvert.SerializeObject(obj);
            byte[] bytes = Encoding.UTF8.GetBytes(json);
            await _stream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
        }
    
        public void Flush()
        {
            _stream.Flush();
        }
    
        public async Task FlushAsync()
        {
            await _stream.FlushAsync().ConfigureAwait(false);
        }
    }
    
    public interface IReader
    {
        object Read(Type objectType);
        Task<object> ReadAsync(Type objectType);
    }
    
    public class JsonStreamReader : IReader
    {
        private readonly Stream _stream;
    
        public JsonStreamReader(Stream stream)
        {
            _stream = stream;
        }
    
        public object Read(Type objectType)
        {
            byte[] bytes = new byte[1024];
            int bytesRead = _stream.Read(bytes, 0, bytes.Length);
            string json = Encoding.UTF8.GetString(bytes, 0, bytesRead);
            object obj = JsonConvert.DeserializeObject(json, objectType);
            return obj;
        }
    
        public async Task<object> ReadAsync(Type objectType)
        {
            byte[] bytes = new byte[1024];
            int bytesRead = await _stream.ReadAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
            string json = Encoding.UTF8.GetString(bytes, 0, bytesRead);
            object obj = JsonConvert.DeserializeObject(json, objectType);
            return obj;
        }
    }
    
    public interface IMessenger
    {
        void Send(object message);
        Task SendAsync(object message);
        object Receive();
        Task<object> ReceiveAsync();
    }
    
    public interface IMessageDescriptor
    {
        string GetMessageName(Type messageType);
        Type GetMessageType(string messageName);
    }
    
    public class Header
    {
        public string MessageName { get; set; }
    }
    
    public class ProtocolMessenger : IMessenger
    {
        private readonly IMessageDescriptor _messageDescriptor;
        private readonly IWriter _writer;
        private readonly IReader _reader;
    
        public ProtocolMessenger(IMessageDescriptor messageDescriptor, IWriter writer, IReader reader)
        {
            _messageDescriptor = messageDescriptor;
            _writer = writer;
            _reader = reader;
        }
    
        public void Send(object message)
        {
            Header header = new Header();
            header.MessageName = _messageDescriptor.GetMessageName(message.GetType());
    
            _writer.Write(header);
            _writer.Write(message);
            _writer.Flush();
        }
    
        public async Task SendAsync(object message)
        {
            Header header = new Header();
            header.MessageName = _messageDescriptor.GetMessageName(message.GetType());
    
            await _writer.WriteAsync(header).ConfigureAwait(false);
            await _writer.WriteAsync(message).ConfigureAwait(false);
            await _writer.FlushAsync().ConfigureAwait(false);
        }
    
        public object Receive()
        {
            Header header = (Header)_reader.Read(typeof(Header));
            Type messageType = _messageDescriptor.GetMessageType(header.MessageName);
            object message = _reader.Read(messageType);
            return message;
        }
    
        public async Task<object> ReceiveAsync()
        {
            Header header = (Header)await _reader.ReadAsync(typeof(Header)).ConfigureAwait(false);
            Type messageType = _messageDescriptor.GetMessageType(header.MessageName);
            object message = await _reader.ReadAsync(messageType).ConfigureAwait(false);
            return message;
        }
    }
    
    IReader
    JsonStreamReader

    public interface IWriter
    {
        void Write(object obj);
        Task WriteAsync(object obj);
        void Flush();
        Task FlushAsync();
    }
    
    public class JsonStreamWriter : IWriter
    {
        private readonly Stream _stream;
    
        public JsonStreamWriter(Stream stream)
        {
            _stream = stream;
        }
    
        public void Write(object obj)
        {
            string json = JsonConvert.SerializeObject(obj);
            byte[] bytes = Encoding.UTF8.GetBytes(json);
            _stream.Write(bytes, 0, bytes.Length);
        }
    
        public async Task WriteAsync(object obj)
        {
            string json = JsonConvert.SerializeObject(obj);
            byte[] bytes = Encoding.UTF8.GetBytes(json);
            await _stream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
        }
    
        public void Flush()
        {
            _stream.Flush();
        }
    
        public async Task FlushAsync()
        {
            await _stream.FlushAsync().ConfigureAwait(false);
        }
    }
    
    public interface IReader
    {
        object Read(Type objectType);
        Task<object> ReadAsync(Type objectType);
    }
    
    public class JsonStreamReader : IReader
    {
        private readonly Stream _stream;
    
        public JsonStreamReader(Stream stream)
        {
            _stream = stream;
        }
    
        public object Read(Type objectType)
        {
            byte[] bytes = new byte[1024];
            int bytesRead = _stream.Read(bytes, 0, bytes.Length);
            string json = Encoding.UTF8.GetString(bytes, 0, bytesRead);
            object obj = JsonConvert.DeserializeObject(json, objectType);
            return obj;
        }
    
        public async Task<object> ReadAsync(Type objectType)
        {
            byte[] bytes = new byte[1024];
            int bytesRead = await _stream.ReadAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
            string json = Encoding.UTF8.GetString(bytes, 0, bytesRead);
            object obj = JsonConvert.DeserializeObject(json, objectType);
            return obj;
        }
    }
    
    public interface IMessenger
    {
        void Send(object message);
        Task SendAsync(object message);
        object Receive();
        Task<object> ReceiveAsync();
    }
    
    public interface IMessageDescriptor
    {
        string GetMessageName(Type messageType);
        Type GetMessageType(string messageName);
    }
    
    public class Header
    {
        public string MessageName { get; set; }
    }
    
    public class ProtocolMessenger : IMessenger
    {
        private readonly IMessageDescriptor _messageDescriptor;
        private readonly IWriter _writer;
        private readonly IReader _reader;
    
        public ProtocolMessenger(IMessageDescriptor messageDescriptor, IWriter writer, IReader reader)
        {
            _messageDescriptor = messageDescriptor;
            _writer = writer;
            _reader = reader;
        }
    
        public void Send(object message)
        {
            Header header = new Header();
            header.MessageName = _messageDescriptor.GetMessageName(message.GetType());
    
            _writer.Write(header);
            _writer.Write(message);
            _writer.Flush();
        }
    
        public async Task SendAsync(object message)
        {
            Header header = new Header();
            header.MessageName = _messageDescriptor.GetMessageName(message.GetType());
    
            await _writer.WriteAsync(header).ConfigureAwait(false);
            await _writer.WriteAsync(message).ConfigureAwait(false);
            await _writer.FlushAsync().ConfigureAwait(false);
        }
    
        public object Receive()
        {
            Header header = (Header)_reader.Read(typeof(Header));
            Type messageType = _messageDescriptor.GetMessageType(header.MessageName);
            object message = _reader.Read(messageType);
            return message;
        }
    
        public async Task<object> ReceiveAsync()
        {
            Header header = (Header)await _reader.ReadAsync(typeof(Header)).ConfigureAwait(false);
            Type messageType = _messageDescriptor.GetMessageType(header.MessageName);
            object message = await _reader.ReadAsync(messageType).ConfigureAwait(false);
            return message;
        }
    }
    
    ComplexClass

    public interface ISomeOtherInterface
    {
        void DoSomething();
    }
    
    public class ComplexClass : IMessenger, ISomeOtherInterface
    {
        private readonly IMessenger _messenger;
        private readonly ISomeOtherInterface _someOtherInterface;
    
        public ComplexClass(IMessenger messenger, ISomeOtherInterface someOtherInterface)
        {
            _messenger = messenger;
            _someOtherInterface = someOtherInterface;
        }
    
        public void DoSomething()
        {
            _someOtherInterface.DoSomething();
        }
    
        public void Send(object message)
        {
            _messenger.Send(message);
        }
    
        // Take 1
        public Task SendAsync(object message)
        {
            return _messenger.SendAsync(message);
        }
    
        // Take 2
        public async Task SendAsync(object message)
        {
            await _messenger.SendAsync(message).ConfigureAwait(false);
        }
    
        public object Receive()
        {
            return _messenger.Receive();
        }
    
        // Take 1
        public Task<object> ReceiveAsync()
        {
            return _messenger.ReceiveAsync();
        }
    
        // Take 2
        public async Task<object> ReceiveAsync()
        {
            return await _messenger.ReceiveAsync().ConfigureAwait(false);
        }
    }
    
    公共接口
    {
    无效剂量();
    }
    公共类ComplexClass:IMessenger,IsoOtherInterface
    {
    私人只读IMessenger\u messenger;
    私有只读IsometherInterface\u someOtherInterface;
    公共ComplexClass(IMessenger messenger、IsoOtherInterface和someOtherInterface)
    {
    _信使=信使;
    _someOtherInterface=someOtherInterface;
    }
    公共无效剂量测定法()
    {
    _someOtherInterface.DoSomething();
    }
    公共无效发送(对象消息)
    {
    _信使。发送(消息);
    }
    //拿1
    公共任务SendAsync(对象消息)
    {
    返回\u messenger.SendAsync(消息);
    }
    //拿2块
    公共异步任务SendAsync(对象消息)
    {
    wait\u messenger.SendAsync(message).configurewait(false);
    }
    公共对象接收()
    {
    return _messenger.Receive();
    }
    //拿1
    公共任务ReceiveAsync()
    {
    返回_messenger.ReceiveAsync();
    }
    //拿2块
    公共异步任务ReceiveAsync()
    {
    返回wait\u messenger.ReceiveAsync().ConfigureWait(false);
    }
    }
    
    这里的一般答案是,使真正的
    异步
    和同步版本的相同功能需要两种不同(可能相似,可能不相似)的实现。您可以尝试找到重复的部件,并使用基类(或实用程序类)重用它们,但实现方式可能会有所不同

    在许多情况下,人们选择只提供一个版本的API,不管它是否异步。例如,整个过程都是异步的。如果你负担得起(许多人负担不起),那将是我的建议

    关于您的具体问题:
  • 不是真的,除了找到相似的部分并把它们抽象出来
  • 不太可能,同步方法需要在同步上下文中测试,而
    async
    方法需要在
    async
    上下文中测试
  • Take 1
    (即直接返回任务)最好有两种方式:
    • 它没有创建整个不需要的
      async
      状态机的开销,这只会略微提高性能
    • ConfigureAwait
      在本例中,仅影响其后面的代码,在本例中,该代码根本不起作用。无论是否使用
      configurewait
      ,它都不会影响调用方的代码
  • 肯定是的(最后是积极性)<代码>异步库中的代码默认情况下应使用
    ConfigureAwait(false)
    ,并仅在特定需要时将其删除

  • 一般来说,API应该是异步的或同步的。例如,如果您的实现包括I/O,那么它应该是异步的

    也就是说,在某些场景中,您确实希望同时使用同步和异步API。例如,如果工作自然是异步的,但需要保留同步API以实现向后兼容性


    如果您处于这种情况,我建议使用来最小化重复代码的数量。和都是反模式。

    你说3是什么意思?我为同一个异步方法提供了两个实现:一个上面有Take 1注释,另一个上面有Take 2注释。我的意思是-哪一个是正确的,为什么?明白了。在我的回答中补充了这一点。不确定你是否在试图引导人们远离的道路上跌跌撞撞。@Damien_不信者我在发布我的问题之前阅读了这两篇文章。即使在我的第一个例子中,你也会看到我没有走这条路。不过,如果你在假设之前读过它们会更好。无论如何,谢谢你指出这一点。我已经看过了关于不同问题的所有不同答案。有些人会说“你永远不需要同步,它应该是异步的。”其他人则指向AsyncHelper上下文,或者你的文章所涉及的任何种类的内容,唯一可靠的答案是你的布尔建议,它与“我的it死锁”无关。我推荐给任何需要用sync包装async的人。是的,布尔参数hack是我个人最喜欢的。不过,这实际上并不是一种用sync包装async的方法。它创建了一个可以是异步或同步的(私有)方法,