هل يجب أن يعيد هذا الرمز مهمة أو مهمة<object>?
-
21-12-2019 - |
سؤال
كنت أقرأ طبيعة المهام, منشور لستيفن توب.
public static Task RunAsync(Action action)
{
var tcs = new TaskCompletionSource<Object>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
action();
tcs.SetResult(null);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
وبما أننا لم يعد يهمني ما هو نوع من
T
هو ، لقد تخلفت عن استخدامObject
.ثم ، عندماAction
يتم تنفيذه بنجاح,SetResult
لا تزال تستخدم لنقلTask
فيRanToCompletion
الحالة النهائية;ومع ذلك ، نظرا لأن قيمة النتيجة الفعلية غير ذات صلة,null
يستخدم. أخيرا,RunAsync
المرتجعاتTask
بدلا منTask<Object>
.وبطبيعة الحال ، فإن مثيلtask
النوع لا يزالTask<Object>
, ، لكننا لا نحتاج إلى الإشارة إليها على هذا النحو ، ولا يحتاج مستهلك هذه الطريقة إلى الاهتمام بتفاصيل التنفيذ هذه.
لا أفهم بشكل خاص لماذا يجب أن تعود الطريقة Task
بدلا من Task<object>
(وهذا هو السبب في أنني أكدت الجملة الجريئة).وأنا أعلم أن يتم تعيين طريقة للعودة Task
لكن tcs
هو TaskCompletionSource<Object>
, ، ليس TaskCompletionSource
(وهذا خطأ ، على ما أعتقد).
المحلول
ليس هناك غير عام TaskCompletionSource
وبالنظر إلى كل ما تريده هو مهمة بدون نتيجة ، فإن النتيجة لا تهم.المتصل لا يعرف ولا يهتم في هذه الحالة أن المهمة هي في الواقع Task<object>
, ، المتصل فقط await
ق ذلك ، ويحصل على استثناء إذا كان هناك واحد.المتصل غير مدرك للنتيجة الفعلية.
يتم تسهيل هذا بالطبع من خلال حقيقة أن Task<T>
يرث من Task
من الشائع أيضا العثور على ملف Task<bool>
التي ترجع كاذبة ، أو Task<int>
مع 0.
نصائح أخرى
لا يوجد غير عام TaskCompletionSource
فئة لإنشاء مثيلات Task
التي ليست حالات Task<T>
.هذا يترك خيارين لمعلمة النوع العام لـ TaskCompletionSource<T>
عندما لا تهتم (أو لا تقدم) قيمة الإرجاع:
- استخدم نوعا موجودا عشوائيا ، مثل
object
, ، كنوع الإرجاع.اضبط القيمة علىnull
للإشارة إلى الانتهاء من المهمة. - استخدم نوعا محددا غير عام ، واضبط القيمة على
null
للإشارة إلى الانتهاء من المهمة.
عندما أقوم بإنشاء ملف TaskCompletionSource<T>
مثال لغرض توفير Task
مع عدم وجود قيمة إرجاع ، أفضل استخدام نوع مخصص غير عام للتأكد من أن الكود المستهلك لن يخطئ في الإرجاع Task
كمثال على Task<T>
حيث النتيجة لها معنى.
أولا ، أنا تحديد الفئة التالية (يمكن أن يكون private sealed class
إذا كان متداخلا داخل نوع آخر):
internal sealed class VoidResult
{
}
ثم ، بدلا من استخدام TaskCompletionSource<object>
لمصدر الانتهاء ، وأنا استخدم TaskCompletionSource<VoidResult>
.منذ VoidResult
لا يمكن الوصول إلى النوع عن طريق الاتصال بالرمز ، ولن يتمكن المستخدم من إرسال Task
الاعتراض على مثيل Task<VoidResult>
.
لا أفهم بشكل خاص لماذا يجب أن تعود الطريقة
Task
بدلا منTask<object>
لأنه عندما تعود Task<Object>
هذا يعني أنه عند اكتمال هذه الطريقة ، ستنتج بعض القيمة المفيدة للنوع Object
.في هذه الحالة ، لا ننتج أي نتيجة ، ولهذا السبب اختار ستيفن العودة Task
.
إذا كنا نتعامل مع Func<Object>
ثم العودة Task<Object>
سيكون من المناسب ، كما Func
سوف تنتج بعض النتائج ، قد نختار لإعادته.
لماذا
TaskCompletionSource<Object>
, ، ليسTaskCompletionSource
?
لأنه لا يوجد شيء من هذا القبيل.لا يوجد غير عام TaskCompletionSource
.
إذا كنت عاد Task<object>
, ، ثم var result = await RunAsync(...)
سوف يعود دائما null
, ، لأن هذا هو ما تقوم بتعيين النتيجة عليه.
العميل لا يهتم بهذا ، لذلك عليك فقط إرجاع Task
.
من الناحية المثالية ، يمكنك استخدام TaskCompletionSource
داخليا ، بدلا من TaskCompletionSource<object>
, ، ومجرد استدعاء شيء من هذا القبيل SetCompleted()
بدلا من SetResult(null)
.لكن هذا النوع غير موجود.