উত্তর:
আপনি ASYNC সঙ্গে পদ্ধতি থাকতে পারে না ref
বা out
প্যারামিটার।
লুসিয়ান Wischik ব্যাখ্যা দিয়েছে কেন এই দুটিই MSDN থ্রেডে সম্ভব নয়: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have -ref-অর-আউট-পরামিতি
কেন হিসাবে অ্যাসিঙ্ক পদ্ধতিগুলি বাই-বাই রেফারেন্স পরামিতিগুলিকে সমর্থন করে না? (বা রেফার্ট প্যারামিটার?) এটি সিএলআর এর একটি সীমাবদ্ধতা। আমরা পুনরুক্তি পদ্ধতিতে অনুরূপ পদ্ধতিতে অ্যাসিঙ্ক পদ্ধতিগুলি প্রয়োগ করতে বেছে নিয়েছি - অর্থাত সংকলকটির মাধ্যমে পদ্ধতিটিকে একটি রাষ্ট্র-মেশিন-অবজেক্টে রূপান্তরিত করে। সিএলআর এর কোনও "আউট প্যারামিটার" বা "রেফারেন্স প্যারামিটার" এর ঠিকানা কোনও বস্তুর ক্ষেত্র হিসাবে সংরক্ষণ করার কোনও নিরাপদ উপায় নেই। বাই-রেফারেন্স পরামিতিগুলিকে সমর্থন করার একমাত্র উপায় হ'ল যদি অ্যাসিঙ্ক বৈশিষ্ট্যটি সংকলক-পুনর্লিখনের পরিবর্তে নিম্ন-স্তরের সিএলআর পুনর্লিখন দ্বারা করা হয়। আমরা এই পদ্ধতির পরীক্ষা করেছিলাম এবং এটির জন্য অনেক কিছু হয়েছিল, তবে শেষ পর্যন্ত এটি এত ব্যয়বহুল হত যে এটি কখনও ঘটেনি।
এই পরিস্থিতির জন্য একটি সাধারণ কাজ হ'ল অ্যাসিঙ্ক পদ্ধতিটি পরিবর্তে একটি টুপল ফিরিয়ে আনা। আপনি নিজের পদ্ধতিটি আবার লিখতে পারেন:
public async Task Method1()
{
var tuple = await GetDataTaskAsync();
int op = tuple.Item1;
int result = tuple.Item2;
}
public async Task<Tuple<int, int>> GetDataTaskAsync()
{
//...
return new Tuple<int, int>(1, 2);
}
Tuple
বিকল্প জন্য ধন্যবাদ । খুব উপকারী.
Tuple
। : পি
আপনার পদ্ধতিতে ref
বা out
পরামিতি থাকতে পারে না async
(যেমনটি ইতিমধ্যে উল্লেখ করা হয়েছিল)।
এই ডেটা ঘুরে বেড়াতে কিছু মডেলিংয়ের জন্য চিৎকার করে:
public class Data
{
public int Op {get; set;}
public int Result {get; set;}
}
public async void Method1()
{
Data data = await GetDataTaskAsync();
// use data.Op and data.Result from here on
}
public async Task<Data> GetDataTaskAsync()
{
var returnValue = new Data();
// Fill up returnValue
return returnValue;
}
আপনি আপনার কোডটি আরও সহজে পুনরায় ব্যবহার করার ক্ষমতা অর্জন করবেন এবং এর সাথে ভেরিয়েবল বা টিপলসের চেয়ে এটি আরও বেশি পঠনযোগ্য।
সি # 7 + সলিউশন অন্তর্ভুক্ত টিউপল সিনট্যাক্স ব্যবহার করা।
private async Task<(bool IsSuccess, IActionResult Result)> TryLogin(OpenIdConnectRequest request)
{
return (true, BadRequest(new OpenIdErrorResponse
{
Error = OpenIdConnectConstants.Errors.AccessDenied,
ErrorDescription = "Access token provided is not valid."
}));
}
রিটার্ন ফলাফল পদ্ধতি স্বাক্ষর সংজ্ঞায়িত সম্পত্তি নাম ব্যবহার করে। উদাহরণ:
var foo = await TryLogin(request);
if (foo.IsSuccess)
return foo.Result;
অ্যালেক্স পঠনযোগ্যতার উপর একটি দুর্দান্ত পয়েন্ট করেছিলেন। সমানভাবে, একটি ফাংশনটি প্রকার (গুলি) ফেরত দেওয়া সংজ্ঞা দিতে যথেষ্ট ইন্টারফেস এবং আপনি অর্থপূর্ণ পরিবর্তনশীল নামও পান।
delegate void OpDelegate(int op);
Task<bool> GetDataTaskAsync(OpDelegate callback)
{
bool canGetData = true;
if (canGetData) callback(5);
return Task.FromResult(canGetData);
}
কলকারীরা একটি ল্যাম্বদা (বা একটি নামযুক্ত ফাংশন) সরবরাহ করে এবং ইন্টেলিজেন্স প্রতিনিধি থেকে পরিবর্তনশীল নাম (গুলি) অনুলিপি করে সহায়তা করে।
int myOp;
bool result = await GetDataTaskAsync(op => myOp = op);
এই নির্দিষ্ট পদ্ধতিটি "চেষ্টা" পদ্ধতির মতো যেখানে myOp
পদ্ধতির ফলাফল থাকলে সেট করা থাকে true
। অন্যথায়, আপনি যত্ন নেই myOp
।
out
প্যারামিটারগুলির একটি দুর্দান্ত বৈশিষ্ট্য হ'ল কোনও ফাংশন ব্যতিক্রম ছুঁড়ে ফেলা সত্ত্বেও এগুলি ডেটা ফেরত দিতে ব্যবহৃত হতে পারে। আমি মনে করি যে কোনও async
পদ্ধতিতে এটি করার নিকটতম সমতুল্য কোনও async
পদ্ধতি এবং কলার উভয়ই উল্লেখ করতে পারে এমন ডেটা ধরে রাখতে একটি নতুন অবজেক্ট ব্যবহার করবে । আরেকটি উপায় হ'ল অন্য উত্তরের পরামর্শ অনুসারে একজন প্রতিনিধিকে পাশ করা ।
নোট করুন যে এই কৌশলগুলির মধ্যে যেটি out
রয়েছে তার সংকলক থেকে প্রয়োগের কোনও ধরণের থাকবে না । উদাহরণস্বরূপ, সংকলকটির আপনাকে ভাগ করা অবজেক্টের মান নির্ধারণ করা বা ডেলিগেটে পাস হওয়া কল করতে হবে না।
এখানে অনুকরণ করার একটি ভাগ বস্তু ব্যবহার করে একটি উদাহরণ বাস্তবায়ন এর ref
এবং out
সাথে ব্যবহারের জন্য async
পদ্ধতি এবং অন্যান্য বিভিন্ন পরিস্থিতিতে যেখানে ref
এবং out
উপস্থিত নেই:
class Ref<T>
{
// Field rather than a property to support passing to functions
// accepting `ref T` or `out T`.
public T Value;
}
async Task OperationExampleAsync(Ref<int> successfulLoopsRef)
{
var things = new[] { 0, 1, 2, };
var i = 0;
while (true)
{
// Fourth iteration will throw an exception, but we will still have
// communicated data back to the caller via successfulLoopsRef.
things[i] += i;
successfulLoopsRef.Value++;
i++;
}
}
async Task UsageExample()
{
var successCounterRef = new Ref<int>();
// Note that it does not make sense to access successCounterRef
// until OperationExampleAsync completes (either fails or succeeds)
// because there’s no synchronization. Here, I think of passing
// the variable as “temporarily giving ownership” of the referenced
// object to OperationExampleAsync. Deciding on conventions is up to
// you and belongs in documentation ^^.
try
{
await OperationExampleAsync(successCounterRef);
}
finally
{
Console.WriteLine($"Had {successCounterRef.Value} successful loops.");
}
}
আমি Try
প্যাটার্নটি পছন্দ করি। এটি একটি পরিপাটি বিন্যাস।
if (double.TryParse(name, out var result))
{
// handle success
}
else
{
// handle error
}
তবে, এটি চ্যালেঞ্জিং async
। এর অর্থ এই নয় যে আমাদের কাছে আসল বিকল্প নেই। এর তিনটি async
অর্ধ সংস্করণে পদ্ধতির জন্য আপনি যে তিনটি মূল পদ্ধতির বিবেচনা করতে পারেন তা এখানেTry
প্যাটার্নটির ।
সবচেয়ে একটি সিঙ্ক মত এই সৌন্দর্য Try
পদ্ধতি শুধুমাত্র একটি ফিরে tuple
একটি পরিবর্তে bool
একটি সঙ্গে out
পরামিতি, যা আমরা সবাই জানি C # অনুমোদিত নয়।
var result = await DoAsync(name);
if (result.Success)
{
// handle success
}
else
{
// handle error
}
একটি পদ্ধতি যে আয় দিয়ে true
এর false
এবং কখনও একটি ছোঁড়ার exception
।
মনে রাখবেন, কোনও
Try
পদ্ধতিতে একটি ব্যতিক্রম ছুঁড়ে দেওয়া প্যাটার্নটির পুরো উদ্দেশ্যটি ভেঙে দেয়।
async Task<(bool Success, StorageFile File, Exception exception)> DoAsync(string fileName)
{
try
{
var folder = ApplicationData.Current.LocalCacheFolder;
return (true, await folder.GetFileAsync(fileName), null);
}
catch (Exception exception)
{
return (false, null, exception);
}
}
anonymous
বাহ্যিক ভেরিয়েবলগুলি সেট করার জন্য আমরা পদ্ধতিগুলি ব্যবহার করতে পারি । এটি কিছুটা জটিল হলেও চালাক সিনট্যাক্স। ছোট মাত্রায় এটি ঠিক আছে।
var file = default(StorageFile);
var exception = default(Exception);
if (await DoAsync(name, x => file = x, x => exception = x))
{
// handle success
}
else
{
// handle failure
}
পদ্ধতিটি Try
প্যাটার্নের বেসিকগুলি মান্য করে তবে out
কলব্যাক পদ্ধতিতে পাস হওয়া পরামিতিগুলি সেট করে। এটি এইভাবে সম্পন্ন হয়েছে।
async Task<bool> DoAsync(string fileName, Action<StorageFile> file, Action<Exception> error)
{
try
{
var folder = ApplicationData.Current.LocalCacheFolder;
file?.Invoke(await folder.GetFileAsync(fileName));
return true;
}
catch (Exception exception)
{
error?.Invoke(exception);
return false;
}
}
এখানে পারফরম্যান্স সম্পর্কে আমার মনে একটি প্রশ্ন রয়েছে। তবে, সি # সংকলকটি এত স্মার্ট হ'ল, আমি মনে করি আপনি প্রায় নিশ্চিতভাবেই এই বিকল্পটি বেছে নিচ্ছেন।
আপনি যদি কেবল TPL
ডিজাইন হিসাবে ব্যবহার করেন ? টিপলস নেই। এখানে ধারণাটি হ'ল আমরা ContinueWith
দুটি পৃথক পথে পুনঃনির্দেশ করতে ব্যতিক্রমগুলি ব্যবহার করি ।
await DoAsync(name).ContinueWith(task =>
{
if (task.Exception != null)
{
// handle fail
}
if (task.Result is StorageFile sf)
{
// handle success
}
});
এমন কোনও পদ্ধতির সাহায্যে exception
যখন কোনও ধরণের ব্যর্থতা হয়। এটি ফিরে আসার চেয়ে আলাদা boolean
। এটি যোগাযোগের উপায় TPL
।
async Task<StorageFile> DoAsync(string fileName)
{
var folder = ApplicationData.Current.LocalCacheFolder;
return await folder.GetFileAsync(fileName);
}
উপরের কোডে, ফাইলটি পাওয়া না গেলে একটি ব্যতিক্রম ছুঁড়ে দেওয়া হবে। এটি ব্যর্থতা ডেকে ContinueWith
আনবে Task.Exception
যা এর লজিক ব্লকে পরিচালনা করবে । ঝরঝরে, হাহ?
শোনো, আমরা
Try
প্যাটার্নটি পছন্দ করি এর একটি কারণ আছে । এটি মৌলিকভাবে এত ঝরঝরে এবং পঠনযোগ্য এবং ফলস্বরূপ, রক্ষণাবেক্ষণযোগ্য। আপনি যেমন আপনার পন্থা চয়ন করেন, ততক্ষণযোগ্যতার জন্য নজরদারি। পরবর্তী বিকাশকারীকে মনে রাখুন যিনি 6 মাসে এবং আপনার কাছে স্পষ্টকরণের প্রশ্নের উত্তর দেওয়ার দরকার নেই। আপনার কোডটি কেবলমাত্র কোনও বিকাশকারীকে থাকতে পারে এমন নথি হতে পারে।
ভাগ্য সুপ্রসন্ন হোক.
ContinueWith
কলগুলির প্রত্যাশিত ফলাফল রয়েছে? আমার বোঝা অনুযায়ী দ্বিতীয়টি ContinueWith
প্রথম কাজটির সাফল্য পরীক্ষা করবে, মূল কাজটির সাফল্য নয়।
আমার চেষ্টা-পদ্ধতি-প্যাটার্নটি ব্যবহার করার মতো একই সমস্যা ছিল যা মূলত অ্যাসিঙ্ক-অপেক্ষার-দৃষ্টান্তের সাথে বেমানান বলে মনে হচ্ছে ...
আমার কাছে গুরুত্বপূর্ণ এটি হ'ল আমি যদি এক-ইফ-ক্লজের মধ্যে চেষ্টা পদ্ধতিটি কল করতে পারি এবং এর আগে আউট-ভেরিয়েবলগুলি প্রাক-সংজ্ঞায়িত করতে না হয় তবে নীচের উদাহরণের মতো এটি ইন-লাইনে করতে পারি:
if (TryReceive(out string msg))
{
// use msg
}
সুতরাং আমি নিম্নলিখিত সমাধান নিয়ে এসেছি:
একটি সহায়ক কাঠামো সংজ্ঞায়িত করুন:
public struct AsyncOut<T, OUT>
{
private readonly T returnValue;
private readonly OUT result;
public AsyncOut(T returnValue, OUT result)
{
this.returnValue = returnValue;
this.result = result;
}
public T Out(out OUT result)
{
result = this.result;
return returnValue;
}
public T ReturnValue => returnValue;
public static implicit operator AsyncOut<T, OUT>((T returnValue ,OUT result) tuple) =>
new AsyncOut<T, OUT>(tuple.returnValue, tuple.result);
}
অ্যাসিঙ্ক ট্রাই-পদ্ধতিটি এর মতো সংজ্ঞায়িত করুন:
public async Task<AsyncOut<bool, string>> TryReceiveAsync()
{
string message;
bool success;
// ...
return (success, message);
}
এ্যাসিঙ্ক ট্রাই-পদ্ধতিটিকে এভাবে কল করুন:
if ((await TryReceiveAsync()).Out(out string msg))
{
// use msg
}
একাধিক আউট প্যারামিটারগুলির জন্য আপনি অতিরিক্ত স্ট্রাক্ট সংজ্ঞায়িত করতে পারেন (উদাঃ AsyncOut <T, OUT1, OUT2>) বা আপনি একটি টুপল ফিরে আসতে পারেন।
প্যারামিটারগুলি async
গ্রহণ না করার পদ্ধতিগুলির সীমাবদ্ধতা out
কেবলমাত্র সংকলক দ্বারা উত্পাদিত অ্যাসিঙ্ক পদ্ধতিগুলিতে প্রযোজ্য, এগুলি async
কীওয়ার্ড সহ ঘোষিত । এটি হস্ত-নকশা করা অ্যাসিঙ্ক পদ্ধতিতে প্রয়োগ হয় না। অন্য কথায় পরামিতিগুলি Task
গ্রহণ করে ফিরে আসা পদ্ধতিগুলি তৈরি করা সম্ভব out
। উদাহরণস্বরূপ বলতে দিন যে আমাদের ইতিমধ্যে একটি ParseIntAsync
পদ্ধতি রয়েছে যা ছোঁড়ে এবং আমরা এমন একটি তৈরি করতে চাই TryParseIntAsync
যা ছুঁড়ে না। আমরা এটি এর মতো বাস্তবায়ন করতে পারি:
public static Task<bool> TryParseIntAsync(string s, out Task<int> result)
{
var tcs = new TaskCompletionSource<int>();
result = tcs.Task;
return ParseIntAsync(s).ContinueWith(t =>
{
if (t.IsFaulted)
{
tcs.SetException(t.Exception.InnerException);
return false;
}
tcs.SetResult(t.Result);
return true;
}, default, TaskContinuationOptions.None, TaskScheduler.Default);
}
TaskCompletionSource
এবং ContinueWith
পদ্ধতিটি ব্যবহার করা কিছুটা বিশ্রীজনক, তবে আমরা await
এই পদ্ধতির অভ্যন্তরে সুবিধাজনক কীওয়ার্ডটি ব্যবহার করতে পারি না বলে অন্য কোনও বিকল্প নেই ।
ব্যবহারের উদাহরণ:
if (await TryParseIntAsync("-13", out var result))
{
Console.WriteLine($"Result: {await result}");
}
else
{
Console.WriteLine($"Parse failed");
}
আপডেট: যদি await
অ্যাসিঙ্ক যুক্তিটি প্রকাশ না করেই জটিল হয় তবে এটি কোনও নেস্টেড অ্যাসিনক্রোনাস বেনামে প্রতিনিধিটির অভ্যন্তরে আবদ্ধ হতে পারে। প্যারামিটারের TaskCompletionSource
জন্য এখনও একটি প্রয়োজন হবে out
। এটি সম্ভব হয় যে out
প্যারামিটারটি মূল টাস্কটি শেষ হওয়ার আগেই সম্পন্ন হতে পারে, যেমন উদাহরণস্বরূপ:
public static Task<string> GetDataAsync(string url, out Task<int> rawDataLength)
{
var tcs = new TaskCompletionSource<int>();
rawDataLength = tcs.Task;
return ((Func<Task<string>>)(async () =>
{
var response = await GetResponseAsync(url);
var rawData = await GetRawDataAsync(response);
tcs.SetResult(rawData.Length);
return await FilterDataAsync(rawData);
}))();
}
এই উদাহরণটিতে তিন অ্যাসিঙ্ক্রোনাস পদ্ধতির অস্তিত্ব অনুমান GetResponseAsync
, GetRawDataAsync
এবং FilterDataAsync
যে পারম্পর্য মধ্যে বলা হয়। out
পরামিতি দ্বিতীয় পদ্ধতি সম্পন্ন সম্পন্ন করা হয়। GetDataAsync
পদ্ধতি এই মত ব্যবহার করা যেতে পারে:
var data = await GetDataAsync("http://example.com", out var rawDataLength);
Console.WriteLine($"Data: {data}");
Console.WriteLine($"RawDataLength: {await rawDataLength}");
প্রতীক্ষমাণ data
অপেক্ষা সামনে rawDataLength
এই সরলীকৃত উদাহরণে গুরুত্বপূর্ণ একটি ব্যতিক্রম ক্ষেত্রে কারণ হল, out
পরামিতি সম্পন্ন করা হবে না।
আমি মনে করি এরকম ভ্যালুটুপল ব্যবহার করা কাজ করতে পারে। আপনাকে প্রথমে ভ্যালুআপল নিউগেট প্যাকেজটি যুক্ত করতে হবে যদিও:
public async void Method1()
{
(int op, int result) tuple = await GetDataTaskAsync();
int op = tuple.op;
int result = tuple.result;
}
public async Task<(int op, int result)> GetDataTaskAsync()
{
int x = 5;
int y = 10;
return (op: x, result: y):
}
এখানে @ ড্যাসট্রোর জবাবের কোডটি সি # 7.0 এর জন্য নামযুক্ত টিপলস এবং টুপল ডেকনস্ট্রাকশন দিয়ে সংশোধিত করা হয়েছে যা স্বরলিপিটি প্রবাহিত করে:
public async void Method1()
{
// Version 1, named tuples:
// just to show how it works
/*
var tuple = await GetDataTaskAsync();
int op = tuple.paramOp;
int result = tuple.paramResult;
*/
// Version 2, tuple deconstruction:
// much shorter, most elegant
(int op, int result) = await GetDataTaskAsync();
}
public async Task<(int paramOp, int paramResult)> GetDataTaskAsync()
{
//...
return (1, 2);
}
নতুন নামের টিপলস, টুপল আক্ষরিক এবং টিপল ডিকনস্ট্রাকশন সম্পর্কে বিশদ জানতে দেখুন: https ://blogs.msdn.mic Microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/
আপনি অপেক্ষা করুন কীওয়ার্ড ব্যবহার না করে সরাসরি টিপিএল (টাস্ক সমান্তরাল গ্রন্থাগার) ব্যবহার করে এটি করতে পারেন।
private bool CheckInCategory(int? id, out Category category)
{
if (id == null || id == 0)
category = null;
else
category = Task.Run(async () => await _context.Categories.FindAsync(id ?? 0)).Result;
return category != null;
}
if(!CheckInCategory(int? id, out var category)) return error