Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 基于非接口实现的泛型约束_C#_.net_Async Await_Factory - Fatal编程技术网

C# 基于非接口实现的泛型约束

C# 基于非接口实现的泛型约束,c#,.net,async-await,factory,C#,.net,Async Await,Factory,我有一个带有工厂服务的应用程序,允许在解决必要的依赖注入的同时构造实例。例如,我使用它来构造对话框视图模型。我有一个如下所示的服务接口: public interface IAsyncFactory { Task<T> Build<T>() where T: class, IAsyncInitialize; } public class AsyncData<T> { readonly Lazy<Task<T>> _da

我有一个带有工厂服务的应用程序,允许在解决必要的依赖注入的同时构造实例。例如,我使用它来构造对话框视图模型。我有一个如下所示的服务接口:

public interface IAsyncFactory
{
    Task<T> Build<T>() where T: class, IAsyncInitialize;
}
public class AsyncData<T>
{
    readonly Lazy<Task<T>> _data;

    // expose async initializer 
    public AsyncData(Func<Task<T>> asyncInit, bool makeThreadSafe = true)
    {
        _data = new Lazy<Task<T>>(asyncInit, makeThreadSafe);
    }

    // expose sync initializer as async
    public AsyncData(Func<T> syncInit, bool makeThreadSafe = true)
    {
        _data = new Lazy<Task<T>>(() => 
            Task.FromResult(syncInit()), makeThreadSafe);
    }

    public Task<T> AsyncValue
    {
        get { return _data.Value; }
    }
}
公共接口IAsyncFactory
{
Task Build(),其中T:class,IAsyncInitialize;
}
理想情况下,我想要的是这样的东西(伪语法,因为这不是直接可以实现的)

公共接口IFactory
{
Task Build(),其中T:class,IAsyncInitialize;
T Build(),其中T:class,!IAsyncInitialize;
}
这里的想法是,如果类支持
IAsyncInitialize
,我希望编译器解析为返回
Task
的方法,以便从使用的代码中可以明显看出它需要等待初始化。如果该类不支持
IAsyncInitialize
,我希望直接返回该类。C#语法不允许这样做,但是有没有其他方法来实现我想要的?这里的主要目标是帮助提醒类的使用者实例化它的正确方法,以便对于具有异步初始化组件的类,在初始化之前我不会尝试使用它


我能想到的最接近的方法是创建单独的
Build
BuildAsync
方法,如果为
IAsyncInitialize
类型调用Build,则会出现运行时错误,但这并没有在编译时捕获错误的好处。

通常,Microsoft建议在命名异步方法时添加
async
后缀。因此,您假设创建两个名为
Build
BuildAsync
的方法是有意义的

我认为没有办法强制执行类似“所有没有实现
IAsyncInitialize
的类型都应该使用
Build
方法而不是
BuildAsync
”的东西,除非您强制开发人员使用另一个接口(如
ISynchronousInitialize
)来标记同步方法

你可以尝试以下方法

  • 不必分离方法,只需实现一个具有以下签名的
    BuildAsync
    方法:

    Task<T> BuildAsync<T>() where T: class
    
    Task BuildAsync(),其中T:class
    
  • BuildAsync
    方法中,检查
    T
    是否实现
    IAsyncInitialize
    。如果是这种情况,只需在创建类型为
    T
    的对象后调用相关的初始化代码即可。否则,只需创建一个
    TaskCompletionSource
    对象,并像异步一样运行同步初始化代码


  • 下面的方法可能不是最好的,但我觉得非常方便。当异步和同步初始值设定项都可用(或者可能可用)时,我将同步初始值设定项包装为与
    Task.FromResult
    异步,并仅向客户端公开异步方法:

    public interface IAsyncInitialize
    {
        Task InitAsync();
        int Data { get; }
    }
    
    // sync version
    class SyncClass : IAsyncInitialize
    {
        readonly int _data = 1;
    
        public Task InitAsync()
        {
            return Task.FromResult(true);
        }
    
        public int Data { get { return _data; } }
    }
    
    // async version
    class AsyncClass: IAsyncInitialize
    {
        int? _data;
    
        public async Task InitAsync()
        {
            await Task.Delay(1000);
            _data = 1;
        }
    
        public int Data
        {
            get 
            {
                if (!_data.HasValue)
                    throw new ApplicationException("Data uninitalized.");
                return _data.Value; 
            }
        }
    }
    
    这只剩下工厂的异步版本:

    public interface IAsyncFactory
    {
        // Build can create either SyncClass or AsyncClass
        Task<T> Build<T>() where T: class, IAsyncInitialize;
    }
    
    在这两种情况下,它总是对客户端代码实施异步,即:

    var sum = await provider1.AsyncData + await provider2.AsyncData;
    
    但是,我不认为这是一个问题,因为
    任务的开销。FromResult
    等待任务。对于同步版本,FromResult
    的开销非常低。我将发布一些基准测试

    使用异步属性的方法可以通过
    Lazy
    进一步改进,例如:

    public interface IAsyncFactory
    {
        Task<T> Build<T>() where T: class, IAsyncInitialize;
    }
    
    public class AsyncData<T>
    {
        readonly Lazy<Task<T>> _data;
    
        // expose async initializer 
        public AsyncData(Func<Task<T>> asyncInit, bool makeThreadSafe = true)
        {
            _data = new Lazy<Task<T>>(asyncInit, makeThreadSafe);
        }
    
        // expose sync initializer as async
        public AsyncData(Func<T> syncInit, bool makeThreadSafe = true)
        {
            _data = new Lazy<Task<T>>(() => 
                Task.FromResult(syncInit()), makeThreadSafe);
        }
    
        public Task<T> AsyncValue
        {
            get { return _data.Value; }
        }
    }
    
    公共类异步数据
    {
    只读惰性数据;
    //公开异步初始值设定项
    公共异步数据(Func asyninit,bool makeThreadSafe=true)
    {
    _data=newlazy(异步,makeThreadSafe);
    }
    //将同步初始值设定项公开为异步
    公共异步数据(Func-syncInit,bool-makeThreadSafe=true)
    {
    _数据=新延迟(()=>
    Task.FromResult(syncInit()),使线程安全;
    }
    公共任务异步值
    {
    获取{return}data.Value;}
    }
    }
    
    你不能真正做到这一点,因为你的
    构建方法只在返回类型上有所不同。这会起作用,但它有一个不幸的副作用,那就是迫使使用者将所有构建操作都当作是异步的,这反过来又迫使他们自己的初始化是异步的,等等。这有利于标准化初始化过程,但当只有一部分类实际需要异步初始化时,这似乎有点沉重。实际上,
    ISynchronousInitialize
    要求可能不是一个好办法。通过实现这两个接口中的一个,这些类基本上通过这个工厂“选择”构造,这对于我的设计是合理的。这也为在初始化过程中允许附加参数提供了一些有趣的可能性,这在执行依赖项注入时总是很棘手的