#c# #.net #compiler-construction
#c# #.net #компилятор-построение
Вопрос:
Рассмотрим этот код.
public class Class1
{
public void ThisShouldNotCompileBecauseOrderWasVoilated()
{
Call2();
Call1();
}
public void ThisShouldCompileBecauseProperOrderIsPresent()
{
Call1();
Call2();
}
private void Call1()
{
// some code
}
private void Call2()
{
// some more code
}
}
Какой код (или атрибут) я должен добавить в Call1() / Call2(), который гарантирует, что компилятор подаст жалобу на 1-й метод и пропустит 2-й метод. Будет некоторый список правил, к которым компилятору придется обратиться, если порядок неправильный. В этом примере в списке правил может быть указано «Call1 Call2», что означает вызов Call1() перед Call2()
Это для языка C # для .NET 4.0
Спасибо!
Комментарии:
1. Просто любопытно, почему вы хотите сделать это во время компиляции? Легко построить сценарии, включающие условную логику, где последовательность вызовов была бы в порядке во время выполнения, но не было бы способа узнать во время компиляции.
2. Я не думаю, что для этого существует какая-либо проверка во время компиляции. Однако проверки во время выполнения должны быть простыми.
3. Если вы пытаетесь защитить себя от внешних вызывающих устройств, а Call1 / Call2 всегда вызываются вместе, то вы могли бы предоставить единственную общедоступную функцию (которая принимает параметры для вызовов 1 и 2 и делегат действия), которая вызывает Call1, вызывает делегат (чтобы вызывающий мог запускать код между ними), а затем вызывает Call2 . Не могу придумать, что вы можете сделать в вашем примере, где вы имеете дело с вызывающими в пределах одного класса.
4. Это не так просто. У меня устаревшая система, где Call1, Call2 уже закодированы и распространены повсюду. Теперь известно, что если Call2 когда-либо вызывается, Call1 никогда не следует вызывать после этого. Итак, я подумал, могу ли я использовать компилятор, чтобы разобраться в этом.
Ответ №1:
В обычном C # нет ничего, что вы могли бы указать для этого.
Вы можете использовать что-то вроде NDepend для обнаружения этого, но я не уверен.
Комментарии:
1. Да, похоже на то. Оценит другие параметры. Спасибо.
Ответ №2:
Вы можете создать свой собственный атрибут и пометить свои методы с его помощью. Затем создайте правило FxCop. FxCop полностью интегрируется с вашим процессом сборки, и пока оба вызова выполняются в рамках одного метода, правило должно быть довольно простым для конкретизации.
Комментарии:
1. Спасибо, разберусь с этим.
Ответ №3:
компилятор не может принудительно выполнять порядок вызовов методов, поскольку во многих случаях он не может статически определить порядок вызовов. Например:
public void whichOrder(boolean b)
{
if (b) call1();
if (!b) call2();
if (b) call2();
if (!b) call1();
}
Если необходимо, чтобы методы вызывались в правильном порядке, у вас есть несколько вариантов:
- документируйте порядок вызовов, чтобы вызывающие пользователи знали, что делать. Это не обеспечивает соблюдения порядка, но, по крайней мере, информирует об этом программистов.
- добавьте состояние к вашему объекту, чтобы запомнить, какой метод был вызван последним, и подтвердите, что текущий вызванный метод разрешен следующим. Это приводит к принудительной проверке метода во время выполнения.
- Используйте макет фреймворка (например, Moq) для модульного тестирования ваших клиентов. Во время сборки проверяется правильность порядка.
Выбранный вами подход зависит от того, насколько важен правильный порядок, и от последствий вызова методов в неправильном порядке.
Альтернативой является переработка вашего дизайна, чтобы порядок методов не стал проблемой. Например, оберните оба метода третьим, call3(), который вызывает call1() и call2() в правильном порядке. Или, возможно, попросите call2() вызвать call1(), если он еще не был выполнен, и попросите call1() проверить, запущен ли он уже, и автоматически вернуть, если его не нужно запускать. Если клиенты вызывают call2(), а затем call1(), вы все равно сначала получаете эффект call1() от внутреннего вызова call2() к call1()), а вызов клиентом call1() не приводит к операции.
Например.
public void call3()
{
call1();
call2();
}
или
public void call2()
{
call1();
// rest of call2's logic
}
private boolean call1Called = false;
pubic void call1()
{
if (!call1Called)
{
call1Called=true;
call1Impl();
}
}
Ответ №4:
Это не совсем то, о чем вы просите… но вы могли бы ввести другой класс:
public class Another1
{
public Another2 Call1()
{
// some code
return new Another2();
// could pass 'this' to Another2 constructor so it has all state
}
}
public class Another2
{
public void Call2()
{
// some more code
}
}
Теперь, начиная с экземпляра Another1, вы можете делать только obj.Call1().Call2()
и никогда obj.Call2().Call1()
. Что еще лучше, это принудительное выполнение находится в IDE по мере ввода. Взгляните также на шаблоны ‘fluent’.
Комментарии:
1. В моем случае это будет невозможно. Спасибо.