Programing

생성자가 비동기식 일 수 있습니까?

lottogame 2020. 4. 6. 07:55
반응형

생성자가 비동기식 일 수 있습니까?


생성자에서 일부 데이터를 채우려는 프로젝트가 있습니다.

public class ViewModel
{
    public ObservableCollection<TData> Data { get; set; }

    async public ViewModel()
    {
        Data = await GetDataTask();
    }

    public Task<ObservableCollection<TData>> GetDataTask()
    {
        Task<ObservableCollection<TData>> task;

        //Create a task which represents getting the data
        return task;
    }
}

불행히도 오류가 발생합니다.

수정자가이 async항목에 유효하지 않습니다

물론 표준 방법으로 래핑하고 생성자에서 호출하면 :

public async void Foo()
{
    Data = await GetDataTask();
}

잘 작동합니다. 마찬가지로 오래된 내부 방식을 사용하면

GetData().ContinueWith(t => Data = t.Result);

그것도 작동합니다. await생성자 내에서 직접 호출 할 수없는 이유가 궁금했습니다 . 아마도 많은 명백한 사례와 그에 대한 이유가있을 것입니다. 나는 생각할 수 없습니다. 나는 또한 설명을 찾아 보았지만 찾을 수없는 것 같습니다.


생성자는 생성 된 형식을 반환하는 메서드와 매우 유사하게 작동합니다. 그리고 async방법은 단지 어떤 종류의 반환 할 수 없습니다, 그것은 하나 "화재와 잊지"이어야한다 void, 또는 Task.

유형의 생성자가 T실제로를 반환 Task<T>하면 매우 혼란 스럽다고 생각합니다.

비동기 생성자가 async void메소드 와 동일한 방식으로 작동하면 생성자가 의도 한 것을 중단시킵니다. 생성자가 반환되면 완전히 초기화 된 객체를 가져와야합니다. 미래에 정의되지 않은 시점에서 실제로 제대로 초기화 될 객체가 아닙니다. 즉, 운이 좋으면 비동기 초기화가 실패하지 않습니다.

이 모든 것이 추측 일뿐입니다. 그러나 비동기 생성자의 가능성이 가치보다 더 많은 문제를 일으키는 것으로 보입니다.

실제로 async void메소드의 “실행 후 잊어 버리기”의미를 원한다면 (가능한 경우 피해야 함) async void메소드 에서 모든 코드를 쉽게 캡슐화 하고 질문에서 언급 한대로 생성자에서 해당 코드를 호출 할 수 있습니다 .


비동기 생성자를 만들 수 없으므로 개인 생성자가 만든 클래스 인스턴스를 반환하는 정적 비동기 메서드를 사용합니다. 이것은 우아하지 않지만 정상적으로 작동합니다.

   public class ViewModel       
   {       
    public ObservableCollection<TData> Data { get; set; }       

    //static async method that behave like a constructor       
    async public static Task<ViewModel> BuildViewModelAsync()  
    {       
     ObservableCollection<TData> tmpData = await GetDataTask();  
     return new ViewModel(tmpData);
    }       

    // private constructor called by the async method
    private ViewModel(ObservableCollection<TData> Data)
    {
     this.Data=Data;   
    }
   }  

문제는 파일 객체를 만들고 파일을 여는 것과 비슷합니다. 실제로 객체를 실제로 사용하기 전에 두 단계를 수행해야하는 많은 클래스가 있습니다 : create + Initialize (종종 Open과 유사한 것).

이것의 장점은 생성자가 가벼울 수 있다는 것입니다. 원하는 경우 실제로 객체를 초기화하기 전에 일부 속성을 변경할 수 있습니다. 모든 속성을 설정하면 사용할 객체를 준비하기 위해 Initialize/ Open함수가 호출됩니다. Initialize기능은 비동기식 일 수 있습니다.

단점은 클래스의 Initialize()다른 기능을 사용하기 전에 호출 할 클래스의 사용자를 신뢰해야한다는 것 입니다. 실제로 클래스를 완전 증명 (바보 증거)으로 만들려면 Initialize()호출 된 모든 기능을 확인해야합니다 .

이를보다 쉽게하기위한 패턴은 생성자를 private으로 선언하고 생성 된 객체 Initialize()를 반환하기 전에 객체를 생성하고 호출하는 공용 정적 함수를 만드는 것 입니다. 이렇게하면 객체에 액세스 할 수있는 모든 사람이이 Initialize기능 을 사용했음을 알 수 있습니다.

이 예제는 원하는 비동기 생성자를 모방하는 클래스를 보여줍니다.

public MyClass
{
    public static async Task<MyClass> CreateAsync(...)
    {
        MyClass x = new MyClass();
        await x.InitializeAsync(...)
        return x;
    }

    // make sure no one but the Create function can call the constructor:
    private MyClass(){}

    private async Task InitializeAsync(...)
    {
        // do the async things you wanted to do in your async constructor
    }

    public async Task<int> OtherFunctionAsync(int a, int b)
    {
        return await ... // return something useful
    }

사용법은 다음과 같습니다.

public async Task<int> SomethingAsync()
{
    // Create and initialize a MyClass object
    MyClass myObject = await MyClass.CreateAsync(...);

    // use the created object:
    return await myObject.OtherFunctionAsync(4, 7);
}

이 특정 경우, 작업을 시작하고 완료시보기에 알리려면 viewModel이 필요합니다. "비동기 생성자"가 아닌 "비동기 속성"이 순서대로 있습니다.

방금 AsyncMVVM을 출시 하여이 문제를 정확하게 해결합니다 (다른 것들 중에서). 사용하면 ViewModel이 다음과 같이됩니다.

public class ViewModel : AsyncBindableBase
{
    public ObservableCollection<TData> Data
    {
        get { return Property.Get(GetDataAsync); }
    }

    private Task<ObservableCollection<TData>> GetDataAsync()
    {
        //Get the data asynchronously
    }
}

이상하게도 Silverlight가 지원됩니다. :)


생성자를 비동기로 만들면 객체를 만든 후에 인스턴스 객체 대신 null 값과 같은 문제가 발생할 수 있습니다. 예를 들어;

MyClass instance = new MyClass();
instance.Foo(); // null exception here

그것이 그들이 추측 할 수없는 이유입니다.


await생성자 내에서 직접 호출 할 수없는 이유가 궁금했습니다 .

.Net 팀 이이 기능을 프로그래밍하지 않았기 때문에 짧은 대답은 간단하다고 생각합니다.

올바른 구문을 사용하면 이것이 구현 될 수 있으며 너무 혼란 스럽거나 오류가 발생해서는 안됩니다. 나는 Stephen Cleary의 블로그 게시물 과 여기에있는 몇 가지 다른 답변이 근본적인 이유가 없으며 그 이상이 해결 방법으로 부족하다는 것을 암시 적으로 지적했다고 생각합니다. 이러한 비교적 간단한 해결 방법의 존재는 아마도이 기능이 아직 구현되지 않은 이유 중 하나 일 것입니다.


생성자에서 비동기 호출로 교착 상태가 발생할 수 있습니다. http://social.msdn.microsoft.com/Forums/en/winappswithcsharp/thread/0d24419e-36ad-4157-abb5-3d9e6c5dacf1참조하십시오.

http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx


일부 답변에는 새로운 public방법을 만드는 것이 포함됩니다 . 이 작업을 수행하지 않고 Lazy<T>클래스를 사용하십시오 .

public class ViewModel
{
    private Lazy<ObservableCollection<TData>> Data;

    async public ViewModel()
    {
        Data = new Lazy<ObservableCollection<TData>>(GetDataTask);
    }

    public ObservableCollection<TData> GetDataTask()
    {
        Task<ObservableCollection<TData>> task;

        //Create a task which represents getting the data
        return task.GetAwaiter().GetResult();
    }
}

사용하려면 Data, 사용 Data.Value.


비동기 키워드에 익숙하지 않습니다 (실버 라이트 또는 Visual Studio 베타 버전의 새로운 기능입니까?)

만약 내가한다면:

var o = new MyObject();
MessageBox(o.SomeProperty.ToString());

o 다음 코드 행이 실행되기 전에 초기화를 완료하지 못할 수 있습니다. 생성자가 완료 될 때까지 객체의 인스턴스를 할당 할 수 없으며 생성자를 비동기로 만들면 요점은 무엇입니까? 그러나 생성자에서 비동기 메소드를 호출하면 생성자가 완료되고 비동기 메소드가 여전히 객체를 설정하는 데 필요한 모든 작업을 수행하는 동안 인스턴스화를 얻을 수 있습니다.


생성자 안에서 Action을 사용할 수 있습니다

 public class ViewModel
    {
        public ObservableCollection<TData> Data { get; set; }
       public ViewModel()
        {              
            new Action(async () =>
            {
                  Data = await GetDataTask();
            }).Invoke();
        }

        public Task<ObservableCollection<TData>> GetDataTask()
        {
            Task<ObservableCollection<TData>> task;
            //Create a task which represents getting the data
            return task;
        }
    }

이 쉬운 트릭을 사용합니다.

public sealed partial class NamePage
{
  private readonly Task _initializingTask;

  public NamePage()
  {
    _initializingTask = Init();
  }

  private async Task Init()
  {
    /*
    Initialization that you need with await/async stuff allowed
    */
  }
}

나는 이런 식으로 사용할 것입니다.

 public class MyViewModel
    {
            public MyDataTable Data { get; set; }
            public MyViewModel()
               {
                   loadData(() => GetData());
               }
               private async void loadData(Func<DataTable> load)
               {
                  try
                  {
                      MyDataTable = await Task.Run(load);
                  }
                  catch (Exception ex)
                  {
                       //log
                  }
               }
               private DataTable GetData()
               {
                    DataTable data;
                    // get data and return
                    return data;
               }
    }

이것은 생성자를 얻을 수있는 것과 비슷합니다.


이것은 창조 패턴을위한 좋은 장소입니다. 이것은 게으른 초기화입니다. 생성자는 비동기식 일 수 없으며 원자 적이어야하며 구체적이고 상태가 있어야합니다.

참고 URL : https://stackoverflow.com/questions/8145479/can-constructors-be-async

반응형