Comments 15
Не пробовал и, если честно, не хотелось бы.
Я думал о возможностях использования, но возможность отстрела ноги слишком велика. Представляет интерес скорее с научной точки зрения.
Бенчмарк стоит дополнить возможными способами оптимизции:
- Сохранить MethodInfo, FieldInfo вместо вычисления каждый раз
- Преобразовать в делегат
- Использовать ExpressionTree, ILGenerator
Последние варианты проигрывают прямому вызову совсем немного.
Разрядка для мозгов: Что будет выведено при вызове виртуального свойства CustomClass?
А можно для не просвящённых почему так?
Я понимаю, какой был бы результат, если бы свойство было не виртуальным, но почему возвращается null
для виртуального я не догоняю.
Мне бы хотелось получить что-нибудь вроде MethodNotFoundException
.
Весь этот пример основан лишь на смещениях. Например, в смещении метода CompareTo я уверен, он совпадает с моим CompareTo. Однако свойство, когда идет первым, проецируется на другой метод. В моем случае это Equals. Попробуйте сделать вместо данного свойства аналог метода Equals. А далее попробуйте передать такую-же строку («4564» в моем случае), или другую. Результаты будут ожидаемыми для метода Equals (true и false соотвественно).
Причина же по которой возвращаемое значение становится null (строго говоря, дефолтным, для int будет 0) мною не разгадана. Если вам интересно, попробуйте подебажить в dnSpy, там можно своими глазами увидеть, в какой метод переходит выполнение. Порядок методов в таблице методов не тривиален. Есть правила, по которым располагаются методы, но я предпочитаю видеть точно.
Это звучит довольно дико для шарпа. И я не утвержаю, а лишь предполагаю. Возможно, это послужит толчком для дальнейших исследований (моих или ваших).
Хм, если честно у меня более прозаическая догадка.
Игрался с таким кодом
public class CustomClass
{
public override string ToString()
{
return "CUSTOM";
}
public virtual object SomeVirtualMethod()
{
return "SomeVirtualMethod";
}
public object SomeMethod()
{
return "SomeMethod";
}
}
[StructLayout(LayoutKind.Explicit)]
public class CustomStructWithLayout
{
[FieldOffset(0)]
public string Str;
[FieldOffset(0)]
public CustomClass SomeInstance;
}
class Program
{
static void Main(string[] args)
{
CustomStructWithLayout instance = new CustomStructWithLayout();
instance.SomeInstance = new CustomClass();
instance.Str = "4564";
Console.WriteLine(instance.SomeInstance.GetType()); //System.String
Console.WriteLine(instance.SomeInstance.ToString()); //4564
Console.WriteLine(instance.SomeInstance.SomeMethod()); // SomeMethod
Console.WriteLine(instance.SomeInstance.SomeVirtualMethod()); // null
}
}
И как мне кажется, для виртуального метода CLR генерит метод-заглушку, возвращающий дефолтное значение.
Поскольку, насколько я понимаю, для вызова виртуального метода CLR необходимо действительно найти этот метод в иерархии типа, и так как метода SomeVirtualMethod
нет в иерархии типа string
, то и возвращается эта заглушка.
Виртуальные методы вызываются по смещению в таблице методов. На этом и основана вся эта статья.
И благодаря этому при вызове метода CompareTo на CustomClass, вызывается метод строки. Это же и объясняет поведение при замене виртуального свойства на клон Equals. Заглушка действительно генерируется(для всех методов), но ради последующей компиляции JITом(имеет единсвеннную инструкцию на тригеринг JITа), которая потом затирается и изменяется на jmp в нужное место памяти, где расположен скомпилированный метод.
Допустим есть метод
[SecurityCritical]
private string CreateTrimmedString(int start, int end)
{
int length = end - start + 1;
if (length == this.Length)
return this;
if (length == 0)
return string.Empty;
return this.InternalSubString(start, length);
}
Чтобы вызвать этот метод, можно написать:
var method = typeof(string).GetMethod("CreateTrimmedString", BindingFlags.Instance | BindingFlags.NonPublic);
var text = "123";
method.Invoke(text, new object[] { 1, 1 })
// "2"
Можно протестировать в С# Interactive
Не уважаю инкапсуляцию, или использование таблицы методов другого типа для быстрого вызова приватных методов