Внутренние работы виртуальных C# и переопределение

Ваша интерпретация верна. Что контролирует блокировку сеанса, так это соединение с брокером. Как только соединение потеряно, сеанс может быть открыт другим клиентом. LockDuration для отдельных сообщений, а не для сеанса. Таким образом, пока клиент подключен к брокеру с открытым сеансом, этот сеанс будет оставаться заблокированным. Как только соединение потеряно, сеанс будет открыт другим запрашивающим клиентом. Но только один клиент за раз, чтобы обеспечить упорядоченную обработку сообщений.

9
задан henry000 15 April 2009 в 14:02
поделиться

6 ответов

Без внесения изменений в ваш образец и дисконтирования отражений нет никакого пути. Цель виртуальной системы состоит в том, чтобы принудительно вызывать производное, несмотря ни на что, и CLR хорошо справляется со своей задачей.

Хотя есть несколько способов обойти это.

Вариант 1: Вы можете добавить следующий метод для ThirdClass

public void SayNoBase() {
  base.SayNo();
}

Это вызвало бы вызов SecondClass.SayNo

Вариант 2: Основная проблема здесь заключается в том, что вы хотите вызывать виртуальный метод не виртуально. C # предоставляет только один способ сделать это через модификатор base. Это делает невозможным вызов метода в вашем собственном классе не виртуальным способом. Вы можете исправить это, добавив второй метод и проксируя.

public overrides void SayNo() {
  SayNoHelper();
}

public void SayNoHelper() {
  Console.WriteLine("No");
}
7
ответ дан 4 December 2019 в 08:16
поделиться

C # не может этого сделать, но действительно действительно возможно в IL с использованием вызова вместо callvirt . Таким образом, вы можете обойти ограничение C #, используя Reflection.Emit в сочетании с DynamicMethod .

Вот очень простой пример, чтобы проиллюстрировать, как это работает. Если вы действительно намереваетесь использовать это, оберните его внутри приятной функции, стремящейся заставить его работать с различными типами делегатов.

delegate string SayNoDelegate(BaseClass instance);

static void Main() {
    BaseClass target = new SecondClass();

    var method_args = new Type[] { typeof(BaseClass) };
    var pull = new DynamicMethod("pull", typeof(string), method_args);
    var method = typeof(BaseClass).GetMethod("SayNo", new Type[] {});
    var ilgen = pull.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.EmitCall(OpCodes.Call, method, null);
    ilgen.Emit(OpCodes.Ret);

    var call = (SayNoDelegate)pull.CreateDelegate(typeof(SayNoDelegate));
    Console.WriteLine("callvirt, in C#: {0}", target.SayNo());
    Console.WriteLine("call, in IL: {0}", call(target));
}

Печать:

callvirt, in C#: No.
call, in IL: NO!!!
14
ответ дан 4 December 2019 в 08:16
поделиться

Конечно ...

   BaseClass bc = new BaseClass();
   string b = bc.SayNo(); 

"Виртуальный" означает , что реализация, которая будет выполняться, основана на типе ACTUAL базового объекта, а не на типе переменной, в которую он вставлен ... Так что, если фактический объект является ThirdClass, это реализация вы получите, независимо от того, на что вы его бросили. Если вам нужно поведение, которое вы описали выше, не делайте методы виртуальными ...

Если вам интересно, "в чем смысл?" это для «полиморфизма»; так что вы можете объявить коллекцию или параметр метода как некоторый базовый тип и включить / передать ему набор производных типов, и все же, когда в коде, даже если каждый объект назначен переменной ref, объявленной как базовый тип, для каждого,

2
ответ дан 4 December 2019 в 08:16
поделиться

Использование base в C # работает только для непосредственной базы. Вы не можете получить доступ к элементу base-base.

Похоже, что кто-то другой избил меня до отказа, ответив, что это возможно сделать в IL.

Однако, я думаю, способ, которым я сделал код gen, имеет некоторые преимущества, поэтому я опубликую его в любом случае.

По-другому я использовал деревья выражений, которые позволяют использовать компилятор C # для разрешения перегрузки и замены общих аргументов.

Это сложно, и вы не хотите повторять это сами, если можете помочь. В вашем случае код будет работать так:

var del = 
    CreateNonVirtualCall<Program, BaseClass, Action<ThirdClass>>
    (
        x=>x.SayNo()
    );

Возможно, вы захотите сохранить делегат в статическом поле только для чтения, так что вам придется его скомпилировать только один раз.

Вам нужно указать 3 универсальных аргумента:

  1. Тип владельца - это класс, из которого вы бы вызвали код, если бы вы не использовали «CreateNonVirtualCall».

  2. Базовый класс - это класс Вы хотите сделать не виртуальный вызов из

  3. Тип делегата. Это должно представлять сигнатуру вызываемого метода с дополнительным параметром для аргумента this. Это можно устранить, но это требует больше работы в методе code gen.

Метод принимает один аргумент - лямбда, представляющую вызов. Это должен быть звонок, и только звонок. Если вы хотите расширить генерацию кода, вы можете поддерживать более сложные вещи.

Для простоты лямбда-тело ограничено только возможностью доступа к лямбда-параметрам и может только передавать их непосредственно в функцию. Вы можете снять это ограничение, если вы расширите код gen в теле метода для поддержки всех типов выражений. Это займет некоторую работу, хотя. С возвращающимся делегатом вы можете делать все, что захотите, поэтому ограничение не слишком велико.

Важно отметить, что этот код не идеален. Он может использовать гораздо больше проверок и не работает с параметрами «ref» или «out» из-за ограничений дерева выражений.

Я проверил его в примерах с помощью методов void, методов, возвращающих значения, и универсальных методов, и это сработало. Однако я уверен, что вы можете найти некоторые крайние случаи, которые не работают.

В любом случае, вот код IL Gen:

public static TDelegate CreateNonVirtCall<TOwner, TBase, TDelegate>(Expression<TDelegate> call) where TDelegate : class
{
    if (! typeof(Delegate).IsAssignableFrom(typeof(TDelegate)))
    {
        throw new InvalidOperationException("TDelegate must be a delegate type.");
    }

    var body = call.Body as MethodCallExpression;

    if (body.NodeType != ExpressionType.Call || body == null)
    {
        throw new ArgumentException("Expected a call expression", "call");
    }

    foreach (var arg in body.Arguments)
    {
        if (arg.NodeType != ExpressionType.Parameter)
        {
            //to support non lambda parameter arguments, you need to add support for compiling all expression types.
            throw new ArgumentException("Expected a constant or parameter argument", "call");
        }
    }

    if (body.Object != null && body.Object.NodeType != ExpressionType.Parameter)
    {
        //to support a non constant base, you have to implement support for compiling all expression types.
        throw new ArgumentException("Expected a constant base expression", "call");
    }

    var paramMap = new Dictionary<string, int>();
    int index = 0;

    foreach (var item in call.Parameters)
    {
        paramMap.Add(item.Name, index++);
    }

    Type[] parameterTypes;


    parameterTypes = call.Parameters.Select(p => p.Type).ToArray();

    var m = 
        new DynamicMethod
        (
            "$something_unique", 
            body.Type, 
            parameterTypes,
            typeof(TOwner)
        );

    var builder = m.GetILGenerator();
    var callTarget = body.Method;

    if (body.Object != null)
    {
        var paramIndex = paramMap[((ParameterExpression)body.Object).Name];
        builder.Emit(OpCodes.Ldarg, paramIndex);
    }

    foreach (var item in body.Arguments)
    {
        var param = (ParameterExpression)item;

        builder.Emit(OpCodes.Ldarg, paramMap[param.Name]);
    }

    builder.EmitCall(OpCodes.Call, FindBaseMethod(typeof(TBase), callTarget), null);

    if (body.Type != typeof(void))
    {
        builder.Emit(OpCodes.Ret);
    }

    var obj = (object) m.CreateDelegate(typeof (TDelegate));
    return obj as TDelegate;
}
2
ответ дан 4 December 2019 в 08:16
поделиться

Вы не можете получить базовые методы переопределения. Независимо от того, как вы приведете объект, всегда используется последнее переопределение в экземпляре.

1
ответ дан 4 December 2019 в 08:16
поделиться

Если оно опирается на поле, вы можете вытащить поле с помощью отражения.

Даже если вы снимаете methodinfo, используя отражение от typeof (BaseClass), вы все равно будете выполнять переопределенный метод

0
ответ дан 4 December 2019 в 08:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: