Monday, February 7, 2011

Microsoft отжигают 1 (System.Char)

Вспоминается шутка: начинающий программист думает что в килобайте тысяча байт, а опытный - что в километре - 1024 метра.
Так как сам немало читаю книг, в которых пишется как "должно быть", тем интереснее бывает, когда натыкаюсь на явные отклонения от этого самого "должно ...". 
Детальный взгляд на код .NET Framework (не без помощи .NET Reflector) показывает, что не всё в  работающих проектах бывает так, как начинающие думают.

Приведу такой пример. 
Структура System.Char.
Метод, проверяющий, является ли символ строки на указанной позиции числом.
public static bool IsNumber(string s, int index)
{
    if (s == null)
    {
        throw new ArgumentNullException("s");
    }
    if (index >= s.Length)
    {
        throw new ArgumentOutOfRangeException("index");
    }
    char ch = s[index];
    if (!IsLatin1(ch))
    {
        return CheckNumber(CharUnicodeInfo.GetUnicodeCategory(s, index));
    }
    if (!IsAscii(ch))
    {
        return CheckNumber(GetLatin1UnicodeCategory(ch));
    }
    return ((ch >= '0') && (ch <= '9'));
}
Аналогичный ему метод, проверяющий, является ли числом один символ.

public static bool IsNumber(char c)
{
    if (!IsLatin1(c))
    {
        return CheckNumber(CharUnicodeInfo.GetUnicodeCategory(c));
    }
    if (!IsAscii(c))
    {
        return CheckNumber(GetLatin1UnicodeCategory(c));
    }
    return ((c >= '0') && (c <= '9'));
}

На лицо явное дублирование кода. Начинающий имеет полное право думать, что из первого можно было бы вызвать второй метод, передав ему символ, и избавится от дублирования кода.
Лично я не берусь судить о том, какими инструментами пользовались те, кто принял решение продублировать код, а также о том, как часто подобные методы вызываются самим .NET Framework и что было бы хуже - увеличить количество повторяющегося кода и тем самым - объем библиотеки и требуемой памяти, или же забить стек в случаях, когда кому то придется часто вызывать такие методы.
Но в любом случае на лицо результат - не все приемы, кажущиеся очевидными и порой даже необходимыми - являются подходящими.


Конечно, если кто то подумает, что эта пара методов единственная и разработчики просто завтыкали, то они будут не правы. Для подтверждения ниже привожу пары других методов этой структуры, содержащих продублированный код:
 Метод
 Код дублируется в
 public static bool IsControl(char c)
 public static bool IsControl(string s, int index)
 public static bool IsDigit(char c)
 public static bool IsDigit(string s, int index)
 public static bool IsLetter(char c)
 public static bool IsLetter(string s, int index)
 public static bool IsLetterOrDigit(char c)
 public static bool IsLetterOrDigit(string s, int index)
 public static bool IsLower(char c)
 public static bool IsLower(string s, int index)
 public static bool IsNumber(char c) 
 public static bool IsNumber(string s, int index)
 public static bool IsSeparator(char c)
 public static bool IsSeparator(string s, int index)
 public static bool IsSymbol(char c)
 public static bool IsSymbol(string s, int index)
 public static bool IsWhiteSpace(char c)
 public static bool IsWhiteSpace(string s, int index)


С другой стороны в следующих методах отказались от дублирования, и вызывают другой метод.
public static bool IsLowSurrogate(string s, int index) вызывает public static bool IsLowSurrogate(char c)
public static bool IsHighSurrogate(string s, int index) вызывает public static bool IsHighSurrogate(char c) 


1 comment:

  1. А не может этот код быть результатом оптимизации компилятором? Ведь рефлектор просто переводит байт код в C#... но не буду утверждать ;)

    ReplyDelete