Как избежать блокировки / распаковки при расширении System.Object?
Я работаю над методом расширения, который применим только к ссылочным типам. Я думаю, однако, в настоящее время это бокс и распаковка значения. Как я могу избежать этого?
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
2 ответа
Это не бокс. Как вы думаете, это бокс? Если это потому, что вы смотрели на 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 может удалить несколько коробчатых корпусов - но, к сожалению, не все)