एक गैंडे या सेटर से एक async विधि कैसे कॉल करें?


223

सी # में एक गेट्टर या सेटर से एक async विधि को कॉल करने का सबसे सुंदर तरीका क्या होगा?

अपने आप को समझाने में मदद करने के लिए यहां कुछ छद्म कोड दिए गए हैं।

async Task<IEnumerable> MyAsyncMethod()
{
    return await DoSomethingAsync();
}

public IEnumerable MyList
{
    get
    {
         //call MyAsyncMethod() here
    }
}

4
मेरा सवाल यह होगा कि क्यों। एक संपत्ति को एक क्षेत्र की तरह कुछ नकल करने के लिए माना जाता है कि इसे आम तौर पर कम (या कम से कम बहुत जल्दी) काम करना चाहिए। यदि आपके पास लंबे समय तक चलने वाली संपत्ति है, तो इसे एक विधि के रूप में लिखना बेहतर है ताकि कॉलर को पता हो कि यह काम का एक अधिक जटिल निकाय है।
जेम्स माइकल हरे

@ नाम: यह बिल्कुल सही है - और मुझे संदेह है कि यह CTP में स्पष्ट रूप से समर्थित नहीं था। कहा जा रहा है, आप हमेशा प्रकार की संपत्ति बना सकते हैं Task<T>, जो तुरंत वापस आ जाएगी, सामान्य संपत्ति शब्दार्थ है, और फिर भी चीजों को आवश्यकतानुसार अतुल्यकालिक रूप से व्यवहार करने की अनुमति दें।
रीड कोपसे जुले

17
@ जेम्स मेरी जरूरत Mvvm और सिल्वरलाइट का उपयोग करने से उत्पन्न होती है। मैं एक संपत्ति के लिए बाध्य होना चाहता हूं, जहां डेटा का लोडिंग आलसी तरीके से किया जाता है। ComboBox एक्सटेंशन क्लास जो मैं उपयोग कर रहा हूं, उसे इनिशियलाइज़कंपोनेंट () स्टेज पर होने वाली बाइंडिंग की आवश्यकता होती है, हालाँकि वास्तविक डेटा लोड बहुत बाद में होता है। जितना संभव हो उतना कम कोड के साथ पूरा करने की कोशिश में, गेट्टर और एसिंक्स एकदम सही संयोजन की तरह लगता है।
डॉगुहान उलुका

संबं
धत लं क

जेम्स एंड रीड, योर लगता है कि यह भूल रहे हैं कि हमेशा किनारे के मामले होते हैं। डब्ल्यूसीएफ के मामले में, मैं यह सत्यापित करना चाहता था कि किसी संपत्ति पर रखा गया डेटा सही है और इसे एन्क्रिप्शन या डिक्रिप्शन का उपयोग करके सत्यापित किया जाना चाहिए। डिक्रिप्शन के लिए मेरे द्वारा उपयोग किए जाने वाले फ़ंक्शंस तृतीय पक्ष विक्रेता से एक async फ़ंक्शन को नियोजित करने के लिए होते हैं। (मैं यहाँ नहीं कर सकता)
राशाद्रिवर

जवाबों:


211

ऐसा कोई तकनीकी कारण नहीं है कि asyncC # में संपत्तियों की अनुमति नहीं है। यह एक उद्देश्यपूर्ण डिजाइन निर्णय था, क्योंकि "एसिंक्रोनस गुण" एक ऑक्सीमोरोन है।

गुणों को वर्तमान मूल्यों को वापस करना चाहिए; उन्हें बैकग्राउंड ऑपरेशंस को किक नहीं करना चाहिए।

आमतौर पर, जब कोई "अतुल्यकालिक संपत्ति" चाहता है, तो वे वास्तव में क्या चाहते हैं:

  1. एक एसिंक्रोनस विधि जो एक मान लौटाती है। इस स्थिति में, गुण को एक asyncविधि में बदलें ।
  2. ऐसा मान जो डेटा-बाइंडिंग में उपयोग किया जा सकता है, लेकिन इसे असिंक्रोनस रूप से गणना / पुनर्प्राप्त किया जाना चाहिए। इस स्थिति में, या तो किसी asyncऑब्जेक्ट के लिए फ़ैक्टरी विधि का उपयोग करें या किसी async InitAsync()विधि का उपयोग करें । डेटा-बाउंड वैल्यू default(T)तब तक होगी जब तक वैल्यू की गणना / पुनर्प्राप्ति नहीं हो जाती।
  3. एक मूल्य जो बनाने के लिए महंगा है, लेकिन भविष्य के उपयोग के लिए कैश किया जाना चाहिए। इस मामले में, AsyncLazy मेरे ब्लॉग या AsyncEx लाइब्रेरी से उपयोग करें । यह आपको एक awaitसक्षम संपत्ति देगा।

अद्यतन: मैं अपने हाल ही के "async OOP" ब्लॉग पोस्टों में अतुल्यकालिक गुणों को शामिल करता हूं।


बिंदु 2. इम्हो में आप नियमित परिदृश्य को ध्यान में नहीं रखते हैं, जहां संपत्ति की स्थापना फिर से अंतर्निहित डेटा को शुरू करना चाहिए (न केवल निर्माण में)। क्या Nito AsyncEx का उपयोग करने या उपयोग करने के अलावा कोई अन्य तरीका है Dispatcher.CurrentDispatcher.Invoke(new Action(..)?
गेरार्ड

@Gardard: मैं नहीं देखता कि बिंदु (2) उस मामले में काम क्यों नहीं करेगा। बस लागू करें INotifyPropertyChanged, और फिर तय करें कि क्या आप पुराने मूल्य को वापस चाहते हैं या default(T)जबकि अतुल्यकालिक अद्यतन उड़ान में है।
स्टीफन क्ली

1
@Stephan: ठीक है, लेकिन जब मैं सेटर में async पद्धति को कॉल करता हूं तो मुझे CS4014 का "इंतजार नहीं" चेतावनी मिलती है (या यह केवल फ्रेमवर्क 4.0 में है?)। क्या आप ऐसे मामले में उस चेतावनी को दबाने की सलाह देते हैं?
गेरार्ड

@Gardard: मेरी पहली सिफारिश NotifyTaskCompletionमेरी AsyncEx परियोजना से उपयोग होगी । या आप अपना खुद का निर्माण कर सकते हैं; यह इतना मुश्किल नही है।
स्टीफन क्लीयर

1
@ स्टेफ़न: ठीक है कि कोशिश करेंगे। शायद इस अतुल्यकालिक डेटाबाइंडिंग-व्यूमॉडल-परिदृश्य के बारे में एक अच्छा लेख जगह में है। उदाहरण के लिए {Binding PropName.Result}यह जानना मेरे लिए बाध्यकारी नहीं है।
गेरार्ड

101

आप इसे एसिंक्रोनस रूप से नहीं कह सकते, क्योंकि एसिंक्रोनस संपत्ति का समर्थन नहीं है, केवल एसिंक्रोनस तरीके हैं। जैसे, दो विकल्प, तथ्य यह है कि CTP में अतुल्यकालिक तरीकों वास्तव में सिर्फ एक तरीका है कि रिटर्न हैं के दोनों लाभ उठा रहे हैं Task<T>या Task:

// Make the property return a Task<T>
public Task<IEnumerable> MyList
{
    get
    {
         // Just call the method
         return MyAsyncMethod();
    }
}

या:

// Make the property blocking
public IEnumerable MyList
{
    get
    {
         // Block via .Result
         return MyAsyncMethod().Result;
    }
}

1
आपकी प्रतिक्रिया के लिए धन्यवाद। विकल्प A: किसी कार्य को वापस करना वास्तव में बाध्यकारी उद्देश्य के लिए कसरत नहीं है। विकल्प B: .Result, जैसा कि आप उल्लेख करते हैं, UI थ्रेड (सिल्वरलाइट में) को ब्लॉक करता है, इसलिए बैकग्राउंड थ्रेड पर कार्य करने के लिए ऑपरेशन की आवश्यकता होती है। मैं देखूंगा कि क्या मैं इस विचार के साथ एक व्यावहारिक समाधान के साथ आ सकता हूं।
डॉगुहान उलुका

3
@duluca: तुम भी एक तरीका है उस तरह के होने की कोशिश कर सकते private async void SetupList() { MyList = await MyAsyncMethod(); } जल्द ही async आपरेशन पूरे कर के रूप में यह MyList सेट होने के लिए कारण होगा (और फिर स्वचालित रूप से बाँध, अगर यह INPC लागू करता है) ...
रीड Copsey

संपत्ति को एक वस्तु में होना चाहिए, जिसे मैंने एक पृष्ठ संसाधन के रूप में घोषित किया है, इसलिए मुझे वास्तव में इस कॉल को गेटटर से उत्पन्न करने की आवश्यकता थी। कृपया उस समाधान के लिए मेरे उत्तर को देखें जिसके साथ मैं आया था।
डोगुहान उलुका

1
@ डुलुका: प्रभावी रूप से, जो मैं आपको सुझाव दे रहा था, वह था ... एहसास, हालांकि, यदि आप शीर्षक को कई बार जल्दी से एक्सेस करते हैं, तो आपका वर्तमान समाधान सिमुलटेनली को कई कॉल करेगा getTitle()...
रीड कोपसे

बहुत अच्छी बात है। हालांकि मेरे विशिष्ट मामले के लिए कोई समस्या नहीं है, isLading के लिए बूलियन चेक समस्या को ठीक करेगा।
डोगुहान उलुका

55

मुझे वास्तव में प्राप्त विधि से उत्पन्न होने के लिए कॉल की आवश्यकता थी, मेरे डिकॉउंड आर्किटेक्चर के कारण। इसलिए मैं निम्नलिखित कार्यान्वयन के साथ आया।

उपयोग: शीर्षक एक ViewModel या एक वस्तु में है जिसे आप सांख्यिकीय रूप से पृष्ठ संसाधन के रूप में घोषित कर सकते हैं। इसे बाइंड करें और वैल्यू यूआई को ब्लॉक किए बिना पॉप्युलेट हो जाएगी, जब गेटटाइट () रिटर्न मिलता है।

string _Title;
public string Title
{
    get
    {
        if (_Title == null)
        {   
            Deployment.Current.Dispatcher.InvokeAsync(async () => { Title = await getTitle(); });
        }
        return _Title;
    }
    set
    {
        if (value != _Title)
        {
            _Title = value;
            RaisePropertyChanged("Title");
        }
    }
}

9
updfrom 18/07/2012 Win8 RP में हमें डिस्पैचर कॉल को इसमें बदलना चाहिए: Window.Current.CoreWindow.Dispatcher.RunAsync (CoreDispatcherPriority.Normal, async) () = {टाइटल = GetTytleAsync (url);});
एंटोन सिज़िकोव

7
@ChristopherStevenson, मैंने भी ऐसा सोचा था, लेकिन मुझे विश्वास नहीं होता कि ऐसा ही है। चूँकि गेट को आग के रूप में निष्पादित किया जा रहा है और भूल जाते हैं, बिना सेटर को बुलाए पूरा होने पर बाइंडिंग को अपडेट नहीं किया जाएगा जब गेट्टर ने एक्सुलेटिंग समाप्त कर दिया हो।
इयान

3
नहीं, यह एक दौड़ की स्थिति है और है, लेकिन उपयोगकर्ता इसे 'RaisePropertyChanged ("शीर्षक")' के कारण नहीं देख पाएंगे। यह पूरा होने से पहले ही लौट जाता है। लेकिन, पूरा होने के बाद आप संपत्ति सेट कर रहे हैं। वह प्रॉपर्टीचेंज इवेंट में आग लगाता है। बाइंडर को फिर से संपत्ति का मूल्य मिलता है।
मेदनी बायकाल

1
मूल रूप से, पहला गेट्टर एक शून्य मान लौटाएगा, फिर इसे अपडेट किया जाएगा। ध्यान दें कि अगर हम चाहते हैं कि गेटटैल को हर बार बुलाया जाए, तो एक ख़राब लूप हो सकता है।
tofutim

1
आपको यह भी पता होना चाहिए कि उस एसिंक्स कॉल में कोई भी अपवाद पूरी तरह से निगल लिया जाएगा। यदि आपके पास एक भी है तो वे आवेदन पर आपके अखंडित अपवाद हैंडलर तक नहीं पहुंचते हैं।
फिल्टर

9

मुझे लगता है कि हम मूल्य के लिए इंतजार कर सकते हैं बस पहले अशक्त लौट रहे हैं और फिर वास्तविक मूल्य प्राप्त कर सकते हैं, इसलिए शुद्ध MVVM (उदाहरण के लिए पीसीएल परियोजना) के मामले में मुझे लगता है कि निम्नलिखित सबसे सुरुचिपूर्ण समाधान है:

private IEnumerable myList;
public IEnumerable MyList
{
  get
    { 
      if(myList == null)
         InitializeMyList();
      return myList;
     }
  set
     {
        myList = value;
        NotifyPropertyChanged();
     }
}

private async void InitializeMyList()
{
   MyList = await AzureService.GetMyList();
}

3
क्या यह संकलक चेतावनी उत्पन्न नहीं करताCS4014: Async method invocation without an await expression
निक

6
Be बहुत यह सलाह निम्न में से उलझन में। इस वीडियो को देखें और फिर अपना मन बना लें: channel9.msdn.com/Series/Three-Essential-Tips-for-Async/…
कंटैंगो

1
आप "async शून्य" विधियों का उपयोग करने से बचना चाहिए!
SuperJMN

1
हर चिल्लाहट को एक बुद्धिमान उत्तर के साथ आना चाहिए, क्या आप @SuperJMN हमें समझाएंगे?
जुआन पाब्लो गार्सिया कोएलो

1
@Contango अच्छा वीडियो। वह कहते हैं, " async voidकेवल शीर्ष स्तर के हैंडलर और उनके जैसे उपयोग करें"। मुझे लगता है कि यह "और उनके जैसे" के रूप में योग्य हो सकता है।
HappyNomad

7

आप Taskइस तरह का उपयोग कर सकते हैं :

public int SelectedTab
        {
            get => selected_tab;
            set
            {
                selected_tab = value;

                new Task(async () =>
                {
                    await newTab.ScaleTo(0.8);
                }).Start();
            }
        }

5

मैंने सोचा .GetAwaiter ()। GetResult () वास्तव में इस समस्या का समाधान था, नहीं? उदाहरण के लिए:

string _Title;
public string Title
{
    get
    {
        if (_Title == null)
        {   
            _Title = getTitle().GetAwaiter().GetResult();
        }
        return _Title;
    }
    set
    {
        if (value != _Title)
        {
            _Title = value;
            RaisePropertyChanged("Title");
        }
    }
}

5
यह बस के साथ अवरुद्ध के रूप में ही है .Result- यह async नहीं है और यह गतिरोध में परिणाम कर सकते हैं।
McGuireV10

आपको
Alexsandr Ter

मैं अपने जवाब पर प्रतिक्रिया की सराहना करता हूं; मैं वास्तव में किसी के लिए एक उदाहरण प्रदान करना पसंद करूंगा जहां यह गतिरोध है इसलिए मैं इसे कार्रवाई में देख सकता हूं
bc3tech

2

चूँकि आपकी "async संपत्ति" एक viewmodel में है, आप AsyncMVVM का उपयोग कर सकते हैं :

class MyViewModel : AsyncBindableBase
{
    public string Title
    {
        get
        {
            return Property.Get(GetTitleAsync);
        }
    }

    private async Task<string> GetTitleAsync()
    {
        //...
    }
}

यह आपके लिए सिंक्रनाइज़ेशन संदर्भ और संपत्ति परिवर्तन अधिसूचना का ध्यान रखेगा।


एक संपत्ति होने के नाते, यह होना चाहिए।
दिमित्री शेट्टमैन

क्षमा करें, लेकिन शायद मैं इस कोड की बात को याद कर रहा था। क्या आप कृपया विस्तार से बता सकते हैं?
पैट्रिक हॉफमैन

गुण परिभाषा द्वारा अवरुद्ध हो रहे हैं। GetTitleAsync () सिंटैक्टिक चीनी को "एसिंक्स गेट्टर" के रूप में कार्य करता है।
दिमित्री शेट्टमैन

1
@DmitryShechtman: नहीं, इसे ब्लॉक करने की आवश्यकता नहीं है। यह वही है जो नोटिफिकेशन और राज्य मशीनों के लिए बदलता है। और वे परिभाषा द्वारा अवरुद्ध नहीं कर रहे हैं। वे परिभाषा द्वारा तुल्यकालिक हैं। यह अवरुद्ध करने के समान नहीं है। "अवरुद्ध" का अर्थ है कि वे भारी काम कर सकते हैं और निष्पादित करने के लिए काफी समय ले सकते हैं। यह बदले में वास्तव में क्या गुण नहीं है।
quetzalcoatl

1

Necromancing।
.NET Core / NetStandard2 में, आप Nito.AsyncEx.AsyncContext.Runइसके बजाय उपयोग कर सकते हैं System.Windows.Threading.Dispatcher.InvokeAsync:

class AsyncPropertyTest
{

    private static async System.Threading.Tasks.Task<int> GetInt(string text)
    {
        await System.Threading.Tasks.Task.Delay(2000);
        System.Threading.Thread.Sleep(2000);
        return int.Parse(text);
    }


    public static int MyProperty
    {
        get
        {
            int x = 0;

            // /programming/6602244/how-to-call-an-async-method-from-a-getter-or-setter
            // /programming/41748335/net-dispatcher-for-net-core
            // https://github.com/StephenCleary/AsyncEx
            Nito.AsyncEx.AsyncContext.Run(async delegate ()
            {
                x = await GetInt("123");
            });

            return x;
        }
    }


    public static void Test()
    {
        System.Console.WriteLine(System.DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff"));
        System.Console.WriteLine(MyProperty);
        System.Console.WriteLine(System.DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff"));
    }


}

यदि आप बस चुना System.Threading.Tasks.Task.Runया System.Threading.Tasks.Task<int>.Run, तो यह काम नहीं करेगा।


-1

मुझे लगता है कि नीचे मेरा उदाहरण @ स्टीफन-क्लीरी के दृष्टिकोण का अनुसरण कर सकता है लेकिन मैं एक कोडित उदाहरण देना चाहता था। यह Xamarin उदाहरण के लिए डेटा बाइंडिंग संदर्भ में उपयोग के लिए है।

वर्ग का निर्माता - या वास्तव में किसी अन्य संपत्ति का सेटर जिस पर यह निर्भर है - एक एस्किंस शून्य कह सकता है जो प्रतीक्षा या ब्लॉक की आवश्यकता के बिना कार्य पूरा होने पर संपत्ति को आबाद करेगा। जब यह अंततः एक मूल्य प्राप्त करता है तो यह आपके UI को NotifyPropertyChanged तंत्र के माध्यम से अपडेट करेगा।

मैं एक निर्माता से aysnc शून्य को बुलाने के किसी भी दुष्प्रभाव के बारे में निश्चित नहीं हूं। शायद एक टिप्पणीकार त्रुटि हैंडलिंग आदि के बारे में विस्तार से बताएगा।

class MainPageViewModel : INotifyPropertyChanged
{
    IEnumerable myList;

    public event PropertyChangedEventHandler PropertyChanged;

    public MainPageViewModel()
    {

        MyAsyncMethod()

    }

    public IEnumerable MyList
    {
        set
        {
            if (myList != value)
            {
                myList = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
                }
            }
        }
        get
        {
            return myList;
        }
    }

    async void MyAsyncMethod()
    {
        MyList = await DoSomethingAsync();
    }


}

-1

जब मैं इस समस्या में भाग गया, तो एक सेटर या एक कंस्ट्रक्टर से एक async विधि तुल्यकालन चलाने की कोशिश कर रहा था मुझे UI थ्रेड पर एक गतिरोध में मिला, और एक घटना हैंडलर का उपयोग करके सामान्य डिजाइन में बहुत अधिक बदलावों की आवश्यकता थी।
समाधान, जैसा कि अक्सर होता है, बस स्पष्ट रूप से लिखने के लिए कि मैं क्या करना चाहता था निहित रूप से, जो एक और धागा संचालन को संभालना था और इसे समाप्त करने के लिए प्रतीक्षा करने के लिए मुख्य धागा प्राप्त करना था:

string someValue=null;
var t = new Thread(() =>someValue = SomeAsyncMethod().Result);
t.Start();
t.Join();

आप तर्क दे सकते हैं कि मैं ढांचे का दुरुपयोग करता हूं, लेकिन यह काम करता है।


-1

मैं सभी उत्तर की समीक्षा करता हूं लेकिन सभी के पास एक प्रदर्शन मुद्दा है।

उदाहरण के लिए:

string _Title;
public string Title
{
    get
    {
        if (_Title == null)
        {   
            Deployment.Current.Dispatcher.InvokeAsync(async () => { Title = await getTitle(); });
        }
        return _Title;
    }
    set
    {
        if (value != _Title)
        {
            _Title = value;
            RaisePropertyChanged("Title");
        }
    }
}

परिनियोजन। Current.Dispatcher.InvokeAsync (async () => {शीर्षक = wait getTitle ();});

डिस्पैचर का उपयोग करें जो एक अच्छा जवाब नहीं है।

लेकिन एक सरल उपाय है, बस इसे करें:

string _Title;
    public string Title
    {
        get
        {
            if (_Title == null)
            {   
                Task.Run(()=> 
                {
                    _Title = getTitle();
                    RaisePropertyChanged("Title");
                });        
                return;
            }
            return _Title;
        }
        set
        {
            if (value != _Title)
            {
                _Title = value;
                RaisePropertyChanged("Title");
            }
        }
    }

यदि आपका कार्य asyn उपयोग getTitle () है। WaitTitle () के बजाय प्रतीक्षा करें ()
Mahdi Rastegari

-4

आप करने के लिए परिवर्तन कर सकते हैं Task<IEnumerable>

और कुछ ऐसा करें:

get
{
    Task<IEnumerable>.Run(async()=>{
       return await getMyList();
    });
}

और इसका उपयोग करें MyList की प्रतीक्षा करें;

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.