생성자가 비동기식 일 수 있습니까?
생성자에서 일부 데이터를 채우려는 프로젝트가 있습니다.
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
'Programing' 카테고리의 다른 글
안드로이드 앱을 개발하기위한 최고의 IDE는 무엇입니까? (0) | 2020.04.06 |
---|---|
Ruby <=> (우주선) 연산자는 무엇입니까? (0) | 2020.04.06 |
두 정수를 나눠서 double을 얻는 방법 (0) | 2020.04.06 |
Twitter Bootstrap 3에서 버튼을 가운데에 맞추는 방법은 무엇입니까? (0) | 2020.04.05 |
Android의 비밀번호 힌트 글꼴 (0) | 2020.04.05 |