Рассмотрим модульную расширяемую систему. Создадим первый модуль, в котором определим константу, и скомпилируем его. Создадим второй модуль, который импортирует первый модуль и использует определённую ранее константу. Скомпилируем второй модуль. А теперь возьмем первый модуль, изменим в нем значение константы на другое и скомпилируем его. Второй модуль перекомпилировать не будем. Запустим на выполнение старый второй модуль вместе с новым первым. Что произойдет? Что реально происходит в таких модульных расширяемых системах как Oberon и .NET я сообщу позже, а сейчас давайте подумаем что должно происходить. Идеальным было бы так, чтобы второй модуль использовал новое значение константы из первого модуля. Не так ли? Что этому мешает? Я думаю, что как минимум на это есть две причины. Во-первых, при компиляции второго модуля константа могла быть нужна компилятору, например, для определения размера переменных в том случае если бы она была использована, например, как размер (не динамического) массива. Во-вторых, используя константу вместо переменной компилятор может осуществить оптимизацию. Первая причина в отличие от второй является фатальной. Выходит, мы не можем ожидать от второго модуля использование константы из первого модуля? Но, не кажется ли Вам, что это какой-то бред? Второй модуль использует константу из первого модуля, которую использовать не может — точно бред, не так ли? Выходит в модульных расширяемых системах просто напросто должно быть запрещено экспортирование / имортирование констант! Ибо это просто невозможно физически реализовать.
Что реально происходит в Oberon (BlackBox 1.5 BETA):
MODULE Module1;
CONST N* = 10; (* Потом меняем ее на 20 *)END Module1.
MODULE Module2;
IMPORT StdLog, Module1;
PROCEDURE Do*;
BEGIN
IF Module1.N = 10 THEN
StdLog.String("Module1.N = 10"); StdLog.Ln
ELSE
StdLog.String("Module1.N # 10"); StdLog.Ln
END
END Do;
END Module2.
При попытке запустить на выполнение старого второго модуля с новым первым происходит ошибка:
"command error: object Module1.N inconsistently imported from Module2"
Что реально происходит в .NET (1.1)
namespace Test
{
public sealed class Module1
{
public const int N = 10; /* Потом меняем ее на 20 */
}
}
namespace Test
{
class Program
{
static void Main(string[] args)
{
if(Module1.N == 10)
{
System.Console.WriteLine("Module1.N = 10");
}
else
{
System.Console.WriteLine("Module1.N # 10");
}
System.Console.ReadLine();
}
}
}
Запускаем на выполнение старый второй модуль с новым первым, в консоли преспокойно печатается какая-то ерунда:
"Module1.N = 10"
ведь в новом первом модуле Module1.N = 20. Совсем не ожиданно, однако...
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Запускаем на выполнение старый второй модуль с новым первым, в консоли преспокойно печатается какая-то ерунда: СГ>
СГ>"Module1.N = 10"
СГ>ведь в новом первом модуле Module1.N = 20. Совсем не ожиданно, однако...
Извини, не прочел все эссе целиком (в лом).
Но, думаю я и так угадаю. Спецификации нужно читать. С целью увеличения быстродействия дотнет копирует (propagate) константы в применяемые модули. Другими словами константа — это ее значение.
Если тебе нужна неизменяемая переменная которая не будет подставляться по месту, то пользуйся модификатором readonly:
class Test
{
public readonly int Test;
}
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, gbear, Вы писали:
G>Константа, она на то и константа. Если бы вычисление её значения переводилось бы в run-time, то это была бы уже не константа. Так... свойство.
Я про то и говорю — значит в модульных расширяемых системах должен быть запрещен импорт / экспорт констант!
Сергей Губанов wrote:
> # Выходит в модульных расширяемых системах просто напросто должно быть > запрещено экспортирование / имортирование констант! Ибо это просто > невозможно физически реализовать.
В нормальных языках (то есть в C/C++) такой проблемы нет — при
модификации константы в h-файле просто все зависимое от нее
перекомпилируется.
В C# тоже нет проблемы — при изменении сборки меняется ее контрольная
сумма, и все зависимое от нее будет перекомпилировано. При попытке
совместить несовместимые сборки — получится exception.
Здравствуйте, VladD2, Вы писали:
VD> Спецификации нужно читать.
Большое спасибо, но я ни сколько не сомневался, что данное поведение обязательно описано в спецификации.
Однако поставленный вопрос, собственно, заключается в смысле экспорта / импорта констант в модульных расширяемых системах, ведь это не возможно реализовать физически.
Здравствуйте, Cyberax, Вы писали:
C>В нормальных языках (то есть в C/C++) такой проблемы нет — при C>модификации константы в h-файле просто все зависимое от нее C>перекомпилируется.
Эти языки не модульные. Нет модулей — нет проблем.
C>В C# тоже нет проблемы — при изменении сборки меняется ее контрольная C>сумма, и все зависимое от нее будет перекомпилировано. При попытке C>совместить несовместимые сборки — получится exception.
Я только что привел пример где этого exception нет, зато преспокойно распечатывается "Module1.N = 10", в то время когда Module1.N = 20.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Cyberax, Вы писали:
C>>В нормальных языках (то есть в C/C++) такой проблемы нет — при C>>модификации константы в h-файле просто все зависимое от нее C>>перекомпилируется.
СГ>Эти языки не модульные. Нет модулей — нет проблем.
C>>В C# тоже нет проблемы — при изменении сборки меняется ее контрольная C>>сумма, и все зависимое от нее будет перекомпилировано. При попытке C>>совместить несовместимые сборки — получится exception.
СГ>Я только что привел пример где этого exception нет, зато преспокойно распечатывается "Module1.N = 10", в то время когда Module1.N = 20.
Вы не использовали strong-name для своей сборки, то есть не рассчитывали её контрольную сумму и не подписывали её — а, следовательно, это ваша сугубо личная проблема. Потому что такие сборки годятся только для написания тестов и отладочных версий приложений. Выпускаемая в свет сборка должна иметь строгое имя...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
На самом деле эту проблему можно решить. Особенно для таких систем, как .Net. Там есть VM, которая делает всякие служебные действия во время выполнения программы. Например, размеры структур, объектов и т.п. считаются во время выполнения джит компилятором. Тогда-же считаются и размеры стековых фреймов. Так что никакой проблемы-то и нет. Джит с таким же успехом может посчитать все константы и вписать значения куда нужно в коде и т.п.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Однако поставленный вопрос, собственно, заключается в смысле экспорта / импорта констант в модульных расширяемых системах, ведь это не возможно реализовать физически.
Для многих задач быстродействие важно, а межмодульная жизнь констрант нет. В общем, есть выбор. Хочешь используешь константны хочешь перменные с readonly.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Сергей Губанов wrote:
> C>В нормальных языках (то есть в C/C++) такой проблемы нет — при > C>модификации константы в h-файле просто все зависимое от нее > C>перекомпилируется. > Эти языки не модульные. Нет модулей — нет проблем.
Модульные, просто модули делаются по-другому.
> C>В C# тоже нет проблемы — при изменении сборки меняется ее контрольная > C>сумма, и все зависимое от нее будет перекомпилировано. При попытке > C>совместить несовместимые сборки — получится exception. > Я только что привел пример где этого exception нет, зато преспокойно > распечатывается "Module1.N = 10", в то время когда Module1.N = 20.
Ну да, в Обероне. В нормальных системах такой проблемы не будет.
Здравствуйте, Mr. None, Вы писали:
MN>Здравствуйте, Сергей Губанов, Вы писали:
СГ>>Здравствуйте, Cyberax, Вы писали:
C>>>В нормальных языках (то есть в C/C++) такой проблемы нет — при C>>>модификации константы в h-файле просто все зависимое от нее C>>>перекомпилируется.
СГ>>Эти языки не модульные. Нет модулей — нет проблем.
C>>>В C# тоже нет проблемы — при изменении сборки меняется ее контрольная C>>>сумма, и все зависимое от нее будет перекомпилировано. При попытке C>>>совместить несовместимые сборки — получится exception.
СГ>>Я только что привел пример где этого exception нет, зато преспокойно распечатывается "Module1.N = 10", в то время когда Module1.N = 20.
MN>Вы не использовали strong-name для своей сборки, то есть не рассчитывали её контрольную сумму и не подписывали её — а, следовательно, это ваша сугубо личная проблема. Потому что такие сборки годятся только для написания тестов и отладочных версий приложений. Выпускаемая в свет сборка должна иметь строгое имя...
Согласен. Только хочу добавить, что при подписывании сборки никакого процесса рассчета ее контрольной суммы не происходит (это нужно только студии). И не факт, что это может спасти. Если из сборки берется только константа, то такая сборка вовсе не нужна приложению и в процессе работы она не будет загружена. При этом приложение будет работать как раньше.
Сергей Губанов, а зачем использовать константы, если, как правильно указал VladD2, есть readonly поля?
Ракот wrote:
> MN>Вы не использовали strong-name для своей сборки, то есть не > рассчитывали её контрольную сумму и не подписывали её — а, > следовательно, это ваша сугубо личная проблема. Потому что такие > сборки годятся только для написания тестов и отладочных версий > приложений. Выпускаемая в свет сборка должна иметь строгое имя... > Согласен. Только хочу добавить, что при подписывании сборки никакого > процесса рассчета ее контрольной суммы не происходит (это нужно только > студии).
Происходит, рассчет контрольной суммы — это часть процесса подписывания.
Подписанная либа получает GUID, по которому ее и находят остальные сборки.
Подписанную сборку изменить нельзя, так что при изменении константы
придется ее переподписать и сгенерировать новый GUID. Ну а от этого уже
остальные сборки пересоберутся.
[... skipped ...]
C>Происходит, рассчет контрольной суммы — это часть процесса подписывания. C>Подписанная либа получает GUID, по которому ее и находят остальные сборки.
C>Подписанную сборку изменить нельзя, так что при изменении константы C>придется ее переподписать и сгенерировать новый GUID. Ну а от этого уже C>остальные сборки пересоберутся.
А если я тем же ключом подпишу? Ведь тогда, по идее, строгое имя сборки не поменяется (если я преднамеренно не поменяю номер версии сборки, что я, конечно же, сделаю в случае её изменения ).
Oyster wrote:
> C>Подписанную сборку изменить нельзя, так что при изменении константы > C>придется ее переподписать и сгенерировать новый GUID. Ну а от этого уже > C>остальные сборки пересоберутся. > А если я тем же ключом подпишу? Ведь тогда, по идее, строгое имя > сборки не поменяется (если я преднамеренно не поменяю номер версии > сборки, что я, конечно же, сделаю в случае её изменения ).
Ну если постараться, то можно что угодно сломать. Достаточно, чтобы
сильное имя осталось тем же — этого можно добиться, если очень захотеть.
Здравствуйте, Cyberax, Вы писали:
C>Oyster wrote:
>> C>Подписанную сборку изменить нельзя, так что при изменении константы >> C>придется ее переподписать и сгенерировать новый GUID. Ну а от этого уже >> C>остальные сборки пересоберутся. >> А если я тем же ключом подпишу? Ведь тогда, по идее, строгое имя >> сборки не поменяется (если я преднамеренно не поменяю номер версии >> сборки, что я, конечно же, сделаю в случае её изменения ).
C>Ну если постараться, то можно что угодно сломать. Достаточно, чтобы C>сильное имя осталось тем же — этого можно добиться, если очень захотеть.
Без приватного ключа? Нелегко это будет... А вот создателю сборки можно без проблем менять что угодно и когда угодно — у него ключ есть и поэтому он всегда может переподписать.
Oyster wrote:
> C>Ну если постараться, то можно что угодно сломать. Достаточно, чтобы > C>сильное имя осталось тем же — этого можно добиться, если очень захотеть. > Без приватного ключа? Нелегко это будет... А вот создателю сборки > можно без проблем менять что угодно и когда угодно — у него ключ есть > и поэтому он всегда может переподписать.
С приватным ключем естественно, без него переподписать не получится.
Здравствуйте, VladD2, Вы писали:
VD>Но, думаю я и так угадаю. Спецификации нужно читать. С целью увеличения быстродействия дотнет копирует (propagate) константы в применяемые модули. Другими словами константа — это ее значение.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Модульная загадка про константу
СГ>[.skip.]
Согласен, что значение константы должно быть частью метаинформации и анализироваться компилятором и рантаймом.
Согласен, что в Обероне лучше, чем в C# и Java. Хотя, по-моему, совсем хорошо было бы, если бы эта проверка выполнялась непосредственно перед выполнением кода, использующего константу. Например, может быть атрибут у метода, содержащего вызов, "использую такую-то константу из такого-то модуля с таким-то значением". При загрузке кода метода атрибут можно было бы прочитать и убедиться, что значения констант совпадают, иначе кинуть exception.
Про фатальность ошибок не согласен. На самом деле — оба типа могут быть как "фатальными", так и не "фатальными".
С рассуждением про то, что константы нельзя использовать в модульных языках не согласен. Могу ещё одно такое же рассуждение толкнуть: допустим мы объявили в первом модуле метод, а во втором его вызвали. Компилятору нужно знать сигнатуру метода, чтобы сгенерировать код его вызова. Теперь добавим в метод параметр и перекомпилируем первый модуль, не компилируя второй. Запустим и, кто бы мог подумать, все фатально навернется при вызове! Выходит, в модульных системах должно быть запрещено использование методов. Чем не бред? Все потому, что выводы мало связаны с посылками, из которых сделаны. То же произошло и в Вашем рассуждении про константы.