كيف يمكنني تجنب الملاكمة/غير الملاكمة عند تمديد System.Object؟
-
25-09-2019 - |
سؤال
أنا أعمل على طريقة تمديد ينطبق فقط على الأنواع المرجعية. ومع ذلك ، أعتقد أنه في الوقت الحالي في الملاكمة وتفصل القيمة. كيف يمكنني تجنب ذلك؟
namespace System
{
public static class SystemExtensions
{
public static TResult GetOrDefaultIfNull<T, TResult>(this T obj, Func<T, TResult> getValue, TResult defaultValue)
{
if (obj == null)
return defaultValue;
return getValue(obj);
}
}
}
مثال الاستخدام:
public class Foo
{
public int Bar { get; set; }
}
في بعض الطرق:
Foo aFooObject = new Foo { Bar = 1 };
Foo nullReference = null;
Console.WriteLine(aFooObject.GetOrDefaultIfNull((o) => o.Bar, 0)); // results: 1
Console.WriteLine(nullReference.GetOrDefaultIfNull((o) => o.Bar, 0)); // results: 0
المحلول
هذا ليس الملاكمة. أين تعتقد ذلك هو ملاكمة؟ إذا كان السبب في ذلك هو أنك نظرت إلى IL حول "==" ، فلا تدعها تخدعك - فإن JIT يحدد ما يجب القيام به هنا. لديها فرصة لإنشاء رمز أصلي مختلف لكل (T
, TResult
) زوج. في الواقع ، سيتم مشاركة الكود لجميع أنواع المرجع ، ويختلف لأنواع القيمة. لذلك سينتهي بك الأمر مع:
T = string, TResult = int (native code #1)
T = Stream, TResult = byte (native code #2)
T = string, TResult = byte (native code #2)
T = Stream, TResult = string (native code #3)
بعد قولي هذا ، إذا كنت ترغب في تقييد طريقة التمديد الخاصة بك على الأنواع المرجعية ، فقم بذلك:
public static TResult GetOrDefaultIfNull<T, TResult>
(this T obj, Func<T, TResult> getValue, TResult defaultValue)
where T : class
لا يزال هناك صندوق في IL ، لكن لا تقلق - لن يحدث أي ملاكمة بالفعل. بعد كل شيء ، ماذا يستطع يكون محاصرًا؟ أنت تقدم مرجعًا ، والمراجع نفسها ليست محاصرًا أبدًا - فقط قيم نوع القيمة محاصر.
نصائح أخرى
ببساطة ، لا يوجد شيء في هذا الرمز يتطلب الملاكمة. هناك نكون السيناريوهات التي لا يمكن تجنبها الملاكمة ، وهناك أيضًا رموز إمكانية إضافية لسد الفجوة بين أنواع القيمة/المرجع (constrained
) في بعض الحالات.
ولكن ليس في هذه الحالة؛ لا فِعلي الملاكمة المطلوبة (يمكن لـ JIT إزالة بعض الحالات التي تشبه مربع - ولكن ليس كلها ، للأسف)