Нужен ли мне Task.Run для вызова метода async из конструктора? [Дубликат]

Нижеприведенное решение делает только одну группуBy и извлекает строки вашего фрейма данных, которые содержат maxValue за один снимок. Нет необходимости в дополнительных Joins или Windows.

import org.apache.spark.sql.Row
import org.apache.spark.sql.catalyst.encoders.RowEncoder
import org.apache.spark.sql.DataFrame

//df is the dataframe with Day, Category, TotalValue

implicit val dfEnc = RowEncoder(df.schema)

val res: DataFrame = df.groupByKey{(r) => r.getInt(0)}.mapGroups[Row]{(day: Int, rows: Iterator[Row]) => i.maxBy{(r) => r.getDouble(2)}}
181
задан Carsten 20 March 2013 в 11:03
поделиться

11 ответов

Конструктор действует очень похоже на метод, возвращающий построенный тип. И async метод не может возвращать только какой-либо тип, он должен быть либо «огонь и забыть» void, либо Task.

Если конструктор типа T фактически возвратил Task<T>, это было бы очень запутанным, я думаю.

Если конструктор async вел себя так же, как метод async void, этот тип разбивает, какой должен быть конструктор. После возврата конструктора вы должны получить полностью инициализированный объект. Не объект, который будет фактически правильно инициализирован в какой-то неопределенной точке в будущем. То есть, если вам повезло, и инициализация async не терпит неудачу.

Все это всего лишь предположение. Но мне кажется, что наличие возможности асинхронного конструктора приносит больше проблем, чем это стоит.

Если вы действительно хотите использовать семантику «огонь и забыть» методов async void (чего следует избегать, если возможно), вы можете легко инкапсулировать весь код в метод async void и вызвать это из своего конструктора, как вы упомянули в вопросе.

148
ответ дан svick 26 August 2018 в 15:19
поделиться

Поскольку невозможно создать конструктор async, я использую статический метод async, который возвращает экземпляр класса, созданный частным конструктором. Это не изящно, но работает нормально.

   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;   
    }
   }  
153
ответ дан Brad Rem 26 August 2018 в 15:19
поделиться

Я не знаком с ключевым словом async (это зависит от Silverlight или новой функции в бета-версии Visual Studio?), но я думаю, что могу дать вам представление о том, почему вы не можете этого сделать.

Если я это сделаю:

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

o не может быть инициализирован до следующей строки кода. Невозможно назначить экземпляр вашего объекта до тех пор, пока ваш конструктор не будет завершен, и создание асинхронного конструктора не изменится, так что будет точкой? Тем не менее, вы можете вызвать асинхронный метод из своего конструктора, а затем ваш конструктор может завершиться, и вы получите свой экземпляр, в то время как метод async все еще делает все, что ему нужно для настройки вашего объекта.

-2
ответ дан Brandon Moore 26 August 2018 в 15:19
поделиться

В этом конкретном случае для запуска задачи требуется 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 поддерживается. :)

4
ответ дан Dmitry Shechtman 26 August 2018 в 15:19
поделиться

, если вы сделаете конструктор асинхронным, после создания объекта вы можете столкнуться с такими проблемами, как пустые значения, а не объекты экземпляра. Например:

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

Вот почему они этого не позволяют.

3
ответ дан Emir Akaydın 26 August 2018 в 15:19
поделиться

Ваша проблема сопоставима с созданием файлового объекта и открытием файла. На самом деле существует много классов, где вам нужно выполнить два шага, прежде чем вы сможете реально использовать объект: create + Initialize (часто называемый чем-то похожим на Open).

Преимущество этого заключается в том, что конструктор может быть легким, а функция Open может выполнять сложную и трудоемкую работу. Эта функция Open может даже быть асинхронной.

Недостатком является то, что вы должны доверять пользователю своего класса, который он назовет Initialize (), прежде чем он будет использовать любую другую функцию вашего класса. На самом деле, если вы хотите сделать свой класс полным доказательством (доказательство дурака?), Вам нужно проверить каждую функцию, которую вызывается Initialize ().

Шаблон, чтобы сделать это проще, - объявить конструктор private и сделать публичную статическую функцию, которая построит объект и вызовет Initialize () перед возвратом построенного объекта. Таким образом, вы узнаете, что все, кто имеет доступ к объекту, использовали функцию Initialize.

В примере показан класс, который имитирует ваш желаемый конструктор async

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 OtherFunctionAsync(a, b);
    }

. Использование будет быть следующим:

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

    // call something else
    return await myObject.OtherFunctionAsync(4, 7);
}
23
ответ дан Harald Coppoolse 26 August 2018 в 15:19
поделиться

вы можете использовать Action in Constructor

 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;
        }
    }
-2
ответ дан Sanjay Patel 26 August 2018 в 15:19
поделиться
1
ответ дан Sylvain Rodrigue 26 August 2018 в 15:19
поделиться

Я бы использовал что-то вроде этого.

 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;
               }
    }

Это как можно ближе к конструкторам.

-2
ответ дан tomcat 26 August 2018 в 15:19
поделиться

Мне просто интересно, почему мы не можем вызвать await изнутри конструктора напрямую.

Я считаю, что короткий ответ просто: потому что команда .Net не запрограммировал эту функцию.

Я считаю, что с правильным синтаксисом это может быть реализовано и не должно быть слишком запутанным или подверженным ошибкам. Я думаю, что сообщение в блоге Стивена Клири и несколько других ответов здесь неявно отметили, что нет основополагающей причины для этого, и более того - решена нехватка обходных решений. Существование этих относительно простых обходных решений, вероятно, является одной из причин, почему эта функция еще не реализована.

0
ответ дан tsemer 26 August 2018 в 15:19
поделиться

Я использую этот простой трюк.

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
    */
  }
}
-2
ответ дан user1630939 26 August 2018 в 15:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: