С# enum или const?
От: Shadedsun  
Дата: 22.06.08 16:45
Оценка: :)
Что бы каждый раз не париться в поисках нужной константы в C++ных хедерах для очередной функции я создал энумератор примерно такого содержания:
public enum WinError : uint
{
    NO_ERROR = 0,
    ERROR_INVALID_FUNCTION = 1,
    ERROR_FILE_NOT_FOUND = 2,
    ERROR_PATH_NOT_FOUND = 3,
    ERROR_TOO_MANY_OPEN_FILES = 4,
    ERROR_ACCESS_DENIED = 5,
    // . . .
    // и так 1800 раз, всего 64k
}

К тому же это удобно при выводе сообщения, оно автоматически преобразуется из числа в строку, но в этом то и есть на мой взгляд пробрема: будут эти строки грузится в память все сразу или по мере надобности? Т.е. я вызываю, к примеру, Console.WriteLine(WinError.ERROR_INVALID_FUNCTION); и только в этом случае строка "ERROR_INVALID_FUNCTION" грузится в оперативу для вывода на экран, или же она в попадает туда при старте приложения?
Есть альтернатива вместо энумераторов использовать константы, но, IMHO, это не так эстетично и не так легко получать их имена. К стати, микрософтные программеры, в таких случаях используют именно константы, возможно, что именно по этой причине, или нет?
Re: С# enum или const?
От: Mab Россия http://shade.msu.ru/~mab
Дата: 22.06.08 16:49
Оценка:
Здравствуйте, Shadedsun, Вы писали:

S>К тому же это удобно при выводе сообщения, оно автоматически преобразуется из числа в строку, но в этом то и есть на мой взгляд пробрема: будут эти строки грузится в память все сразу или по мере надобности?

Все эти строки -- часть метаинформации сборки. Как и все имена классов, полей, методов и т.д.

А вот в чем именно проблема, я так и не понял.
Re: С# enum или const?
От: adontz Грузия http://adontz.wordpress.com/
Дата: 22.06.08 17:19
Оценка:
Здравствуйте, Shadedsun, Вы писали:

Я сделал точно так же, не парюсь.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: С# enum или const?
От: Lloyd Россия  
Дата: 22.06.08 17:21
Оценка:
Здравствуйте, Shadedsun, Вы писали:

S>К тому же это удобно при выводе сообщения, оно автоматически преобразуется из числа в строку, но в этом то и есть на мой взгляд пробрема: будут эти строки грузится в память все сразу или по мере надобности? Т.е. я вызываю, к примеру, Console.WriteLine(WinError.ERROR_INVALID_FUNCTION); и только в этом случае строка "ERROR_INVALID_FUNCTION" грузится в оперативу для вывода на экран, или же она в попадает туда при старте приложения?


В описанном варианте, у занчения, переданного в Console.WriteLine будет вызыван метод ToString внутри этого метода.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[2]: С# enum или const?
От: Shadedsun  
Дата: 22.06.08 17:24
Оценка:
Mab>Все эти строки -- часть метаинформации сборки. Как и все имена классов, полей, методов и т.д.

Зато я вас понял. Спасибо за быстрый ответ.

Mab>А вот в чем именно проблема, я так и не понял.


Энумераторы позволяют получить строки без исползования рефлекшина, поэтому я подумал что имена всех констант будут грузится в память сразу при старте приложения, в моем случае это могло бы быть 64к ненужных данных, плюс еще аналог для WM_ и т.д. итого под пол-метра. Но раз оно все хранится в метаданных, значит мое беспокойство о нерациональном использовании оперативы было напрасным.
Re[3]: С# enum или const?
От: Vovstehn  
Дата: 22.06.08 18:32
Оценка:
Здравствуйте, Shadedsun, Вы писали:

S>Энумераторы позволяют получить строки без исползования рефлекшина, поэтому я подумал что имена всех констант будут грузится в память сразу при старте приложения, в моем случае это могло бы быть 64к ненужных данных, плюс еще аналог для WM_ и т.д. итого под пол-метра. Но раз оно все хранится в метаданных, значит мое беспокойство о нерациональном использовании оперативы было напрасным.


На самом деле, дополнительный расход памяти будет, тут решать тебе на сколько он будет приемлем. Все дело в том, что в типе enum содержится статистическое поле fieldInfoHash, которое является кешом. Ключами для него являются конкретные типы enum-а, а значением — ассоциативный массив имя поля enum-а и его значение. При вызове метода .ToString(), если этот ассоциативный массив не создан, он создается и заполняется с помощью рефлексии и помещается в кешь.

Количество типов в этом кеше ограничено и составляет 101, далее кешь обнуляется. Вот специально делал как-то тест:

// создадим 102 типа enum-а
    public enum MyEnum0
    {
        EnumValue1,
        EnumValue2,
        EnumValue3,
        EnumValue4
    }
//...
    public enum MyEnum101
    {
        EnumValue1,
        EnumValue2,
        EnumValue3,
        EnumValue4
    }

    class Program
    {
        static int count1 = 101;
        static int count2 = 102;

        static void Main(string[] args)
        {
            GenerateEnums(count2);
            Enum[] enums = CreateEnums();
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int k = 0; k < 1000; k++) // тесты прогоняем по 1000 раз, чтобы результат был более внушительный =)
            {
                TestEnums(enums, count1);
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

            sw.Reset();

            sw.Start();
            for (int k = 0; k < 1000; k++)
            {
                TestEnums(enums, count2);
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

            Console.ReadLine();
        }

        static void TestEnums(Enum[] enums, int length)
        {
            for(int i = 0; i < length; i++)
            {
                string s = enums[i].ToString();
            }    
        }

        static Enum[] CreateEnums(int count)
        {
            Enum[] enums = new Enum[count];
            for (int i = 0; i < count; i++)
            {
                enums[i] = (Enum)Activator.CreateInstance(
                    Type.GetType("ConsoleApplication14.MyEnum" + i));
            }
            return enums;
        }
}

Результат:
322 мс - для 101-го enum'а
677 мс - для 102-ух


Как видно изменение количества типов перечеслений на ~1% дало вдвое большее время. Но тест, естественно синтетический, близко принимать к сердцу не стоит
Re[4]: С# enum или const?
От: Vovstehn  
Дата: 22.06.08 18:37
Оценка:
Здравствуйте, Vovstehn, Вы писали:

V> GenerateEnums(count2);


На эту строку не обращайте внимание, просто забыл убрать.
Re[4]: С# enum или const?
От: Аноним  
Дата: 22.06.08 23:36
Оценка:
V>Количество типов в этом кеше ограничено и составляет 101, далее кешь обнуляется. Вот специально делал как-то тест:

Да, черт возьми, есть в этом что-то мистическое. Я запускал такую штуку:
static int[] set = {102, 101, 102, 103, 102, 102, 103, 103, 102, 103, 104};
const int repetitions = 1000;

for (var i = 0; i < set.Length; i++)
{
    sw.Start();
    for (int k = 0; k < repetitions; k++)
    {
        TestEnums(enums, set[i]);
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
    sw.Reset();
}

// . . .

static void TestEnums(Enum[] enums, int length)
{
    for (int i = 1; i < length; i++)
    {
        string[] s = Enum.GetNames(enums[i].GetType());
    }
}

У меня цикл в TestEnums стартовал с 1 поэтому порог был между 102 — 40-43мс и 103 — 270-280мс, в то время как разница между 101 и 102, 103 и 104 вообще не была заметна.
Re[4]: С# enum или const?
От: Shadedsun  
Дата: 22.06.08 23:50
Оценка:
А потом увеличил колличество полей в энумераторе до 50 и получил:
101-102 грузятся за 56-64мс
103-104 грузятся за 1276-1348мс
Re[5]: С# enum или const?
От: Shadedsun  
Дата: 23.06.08 00:48
Оценка: :)
Это меня натолкнуло на грусные мысли и я полез в исходники.
Enum.cs от Микрософт:
ulong[] values = null;
String[] names = null; 
// . . .
InternalGetEnumValues(enumType, ref values, ref names);

Вывод:
Допустим у меня есть энумератор из 2000 полей, в проге мне понадобится вывести пусть аж с десятк имен, но как видно это никого не волнует, оперативу тарят полным набором. И так пока не заполнится буфер на 100 энумераторов не взирая на размер каждого в отдельности, у кого-то 2 поля, а у кого-то 10000, по заполнению произойдет сброс, и все начнется сначала. АЦЦТОЙ.
Как вариант: в релизных прогах имена ваще не трогать, особенно у больших энумераторов.
Re[3]: С# enum или const?
От: vdimas Россия  
Дата: 24.06.08 16:52
Оценка:
Здравствуйте, Shadedsun, Вы писали:


S>Энумераторы позволяют получить строки без исползования рефлекшина, поэтому я подумал что имена всех констант будут грузится в память сразу при старте приложения, в моем случае это могло бы быть 64к ненужных данных, плюс еще аналог для WM_ и т.д. итого под пол-метра. Но раз оно все хранится в метаданных, значит мое беспокойство о нерациональном использовании оперативы было напрасным.


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

А зачем вообще это надо?
Для ветвления по if согласно моей субъективной практике было достаточно около десятка ошибок виндов, а для остального есть уже готовый механизм на базе Win32Exception, который сам достаёт сообщение по коду.

Так же приличное кол-во виндовых ошибок уже определено в enum SocketError.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[4]: С# enum или const?
От: Shadedsun  
Дата: 24.06.08 20:39
Оценка:
Здравствуйте, vdimas, Вы писали:
V>Не напрасным, при первой же попытке получить строку из значения enum в память будут загружены все строковые значение этого типа, и эта операция далеко не самая быстрая.
Меня успокоили, что строки в метаданных, я подумал, что напрасно. Потом Vovstehn начал копать глубже и я тож стал, тогда и обнаружилась эта склоннасть загружать все подряд при попытке получить хотя бы одну строку.

V>А зачем вообще это надо?

V>Для ветвления по if согласно моей субъективной практике было достаточно около десятка ошибок виндов, а для остального есть уже готовый механизм на базе Win32Exception, который сам достаёт сообщение по коду.
Да, согласен, что достаточно десятка ошибок, но у меня есть енумератор со всеми ошибками, я переделал сишный хедер регулярными выражениям, так че в одном случае выгребать из него один десяток, в другом другой?
Win32Exception, насколько я понял, можно применить если ошибка в .NET функции и тогда можно получить API'шное сообщение и др. инфу по ошибке.
Пример из MSDN:
try {
    System.Diagnostics.Process myProc = new System.Diagnostics.Process();
    myProc.StartInfo.FileName = "c:\nonexist.exe";  //Attempting to start a non-existing executable
    myProc.Start();    //Start the application and assign it to the process component.    
}
    catch(Win32Exception w) {
    Console.WriteLine(w.Message);
    Console.WriteLine(w.ErrorCode.ToString());
    Console.WriteLine(w.NativeErrorCode.ToString());
    Console.WriteLine(w.StackTrace);
    Console.WriteLine(w.Source);
    Exception e=w.GetBaseException();
    Console.WriteLine(e.Message);
}

А если работаешь напрямую с API-функциями?

V>Так же приличное кол-во виндовых ошибок уже определено в enum SocketError.

На счет этого не знал. Буду смотреть.
Re[5]: С# enum или const?
От: vdimas Россия  
Дата: 25.06.08 00:50
Оценка:
Здравствуйте, Shadedsun, Вы писали:

S>Да, согласен, что достаточно десятка ошибок, но у меня есть енумератор со всеми ошибками, я переделал сишный хедер регулярными выражениям, так че в одном случае выгребать из него один десяток, в другом другой?


Ну..., сделай один файл и положи себе на видное место, а для реальной приложухи надёргаешь необходимых кодов. Просто я уверен, что и в десятке надобности не будет, ибо обычно логика обработки ошибок на исключениях строится, а не на if-ах. И тебе редко надо в программе узнать причину ошибки, например, при записи файла, обычно достаточно вразумительного сообщения юзверю, и тут как раз уместен Win32Exception, а не мифическое имя константы из enum.


S>А если работаешь напрямую с API-функциями?


А если работаешь напрямую с API-шными ф-иями, то просто используй конструктор от кода возврата или же пустой конструктор, если согласно доки надо взять значение из GetLastError(). Для работы с кодами HRESULT имеется метод Marshal.GetExceptionForHR(int errorCode).
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[6]: С# enum или const?
От: Shadedsun  
Дата: 25.06.08 14:32
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ну..., сделай один файл и положи себе на видное место, а для реальной приложухи надёргаешь необходимых кодов. Просто я уверен, что и в десятке надобности не будет, ибо обычно логика обработки ошибок на исключениях строится, а не на if-ах. И тебе редко надо в программе узнать причину ошибки, например, при записи файла, обычно достаточно вразумительного сообщения юзверю, и тут как раз уместен Win32Exception, а не мифическое имя константы из enum.


Вроде внял. Теперь делаю так:
// на низком уровне:
class DirectoryReader
{
    // . . . 
    public void Start()
    {
        // . . . 
        IntPtr hFindFile = FindFirstFile(path, findData);
        if
        (
            hFindFile.ToInt32() == -1 &&
            (error = Marshal.GetLastWin32Error()) != (int)WinError.ERROR_FILE_NOT_FOUND
        ) throw new Win32Exception(error);
        // . . . 
    } 
    // . . . 
}

// на уровень выше:
public static void Main2()
{
    var dirReader = new DirectoryReader(@"c:\windows\*", DisplayDirectoryItems, 10);
    try { dirReader.Start(); }
    catch (Win32Exception Win32Error)
    {
        Console.WriteLine("Error code: {0}\r\n{1}", Win32Error.NativeErrorCode, Win32Error.Message);
    }
}

V>А если работаешь напрямую с API-шными ф-иями, то просто используй конструктор от кода возврата или же пустой конструктор, если согласно доки надо взять значение из GetLastError(). Для работы с кодами HRESULT имеется метод Marshal.GetExceptionForHR(int errorCode).

Просмотрел статью "How to: Map HRESULTs and Exceptions", насколько я понял Marshal.GetExceptionForHR() используется для оберток над комами, т.е. их неуправляемые функции возвращают HRESULT, а класс обертка преобразует его в соответствующее исключение.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.