14.04.10

Object Initializers в C# 3.0 или как не писать много конструкторов

В C# 3.0 появилась замечательная вещь - инициализаторы объектов (Object Initializers). Это, в принципе, syntactic sugar, и реально позволяет создавать объекты "короче", а значит, код становится простым и читаемым.

Рассмотрим, как раньше мы создавали объект:
class Contact
{
    public string Name { get; set; }
    public int YearofBirth { get; set; }
}

Contact c1 = new Contact();
c1.Name = "John";
c1.YearofBirth = 1980;

Если мы напишем для класса конструктор с двумя параметрами, то инициализация станет короче:
Contact c1 = new Contact("John", 1980);

Минус очевиден - для каждого набора параметров надо писать свой конструктор (значения по умолчанию для параметров появились лишь в C# 4.0). Но теперь нам доступен другой способ инициализации:
Contact c1 = new Contact { Name = "John", YearofBirth = 1980 };

Присваивания свойств идут через запятую в фигурных скобках, внутри скобок ";" не ставится.

Предположим, что конструктор обязательно должен принимать Name. Тогда можно инициализировать объект следующим образом:
Contact c1 = new Contact("Wei-Meng Lee") { YearofBirth=1980 };

Теперь мы имеем эффективное средство для инициализации свойств для ленивых, т.е. для нас, программистов.

Похожим образом теперь можно создавать коллекции - это называется Collection Initializers:

раньше
List Contacts = new List ();
Contacts.Add(new Contact { Name = "John", YearofBirth = 1980 });
Contacts.Add(new Contact { Name = "Mary", YearofBirth = 1986 });
Contacts.Add(new Contact { Name = "Richard",YearofBirth=1948 });

теперь
List Contacts = new List() {
    new Contact { Name = "John", YearofBirth = 1980 },
    new Contact { Name = "Mary", YearofBirth = 1986 },
    new Contact { Name = "Richard", YearofBirth = 1948 }
};

10.04.10

Тестовый smtp-сервер с помощью python

Оказывается, Питон прямо "из коробки" умеет делать fake stmp server, никуда ничего не отправляющий, а логирующий все в stdout (в консоль, да). Все, что для этого надо - поставить python и набрать в командной строке

python -m smtpd -n -c DebuggingServer localhost:25

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

Если кто не понял, зачем это надо: есть задача проверить рассылку писем на много адресов, а спамить никого не охота, да и самому заводить много ящиков лень. Вот тогда вы берете Python и запускаете отладочный smtp-сервер. Пишите письма на здоровье!

13.11.09

Re-использование кода

Елена Сагалаева интересную заметку написала. Похоже, что у всех такие проблемы возникают и идеально не решаются. У нас гораздо меньше, чем 50 человек, но какая-то дисциплина тоже требуется. И решил я, что:

1. Нужен корпоративный портал, который предоставит всю функциональность в удобном виде (что-то типа code.google.com, sourceforge и т.п.)
2. Все модули, которые предполагается повторно использовать, должны версионироваться - если в каком-то проекте уже используется какая-то версия модуля, то его нельзя менять!
3. Модуль должен быть снабжен документацией в вики-формате, состоящей из двух частей - человеческой и автосгенерированной по коду.
4. Документация должна версионироваться вместе с модулем.
5. При создании новой версии модуля необходимо обязательно указывать отличия. Провели рефакторинг, ускорили алгоритм на 20% - будьте добры написать. Возможно, кто-то только этого и ждал.
6. Разработчики проекта, в котом используется такой общий модуль, автоматически подписываются (пожизненно :) на выход новых версий модуля.
7. Модули должны быть доступны как в собранном виде, так и в виде исходников, чтобы все-таки позволить сделать копи-паст.
8. И было бы неплохо иметь утилиту, которая попробует собрать все (!) проекты, использующие данный модуль, с новой версией модуля. И запустит тесты. Ибо удобство превыше всего.

Ну и должна быть страница, которая угадывает мысли. Нужен вам какой-то функционал - пожалуйста, смотрите вот этот модуль. Без этой страницы никакое повторное использование невозможно. Именно у нее будут спрашивать: "а не написал ли уже кто-нибудь то, что мне нужно?"

28.10.09

Как определить город посетителя (определение адреса по ip)

Все хорошие сайты умеют определять адрес, вернее, город, в котором живет посетитель. И делается это, понятно, по ip-адресу. Только вот кто откуда берет географические привязки ip? Лично я решил использовать сервис IpGeoBase. Он позволяет скачать базу целиком или же использовать xml-сервис. Я предпочел пока второй вариант, чтобы не заморачиваться с ежедневным выкачиванием базы. Понятно, что когда поток посетителей у меня возрастет до небывалых высот, я буду выкачивать базу. А пока у меня это работает очень быстро и выглядит так (код на Питоне 2.6):

#coding=utf-8

def getGeoData(ip):
  """Получить город по ip-адресу (адрес предается как строка).
  Возвращает словарь с элементами-строками city, region, district
  "
""

  import httplib
  import re
  from xml.dom.minidom import parseString

  conn = httplib.HTTPConnection("194.85.91.253:8090")
  conn.request("POST", "/geo/geo.html", \
    "<ipquery><fields><all/></fields><ip-list><ip>" + ip + \
    "</ip></ip-list></ipquery>")
  resp = conn.getresponse()

  data = resp.read()
  conn.close()

  # если ничего не найдено
  if re.search("<message>Not found</message>", data):
    return None

  dom = parseString(data)
  city = dom.documentElement.getElementsByTagName('city')[0]\
    .firstChild.nodeValue
  region = dom.documentElement.getElementsByTagName('region')[0]\
    .firstChild.nodeValue
  district = dom.documentElement.getElementsByTagName('district')[0]\
    .firstChild.nodeValue

  return {'city' : city, 'region' : region, 'district' : district}


Теперь вопрос остальным: кто как справляется с этой задачей?

UPD: иногда сервис неверно определяет адрес по ip, потому что некоторые провайдеры используют общий DHCP для всех регионов, или же информация об адресах еще не собрана. Войдя на IpGeoBase, можно указать свой правильный адрес, если по ip он определился неверно.

15.10.09

Memcached для Windows и .NET

Вообще, довольно странно размещать memcached на машине с виндой, так как за операционку надо платить. Гораздо дешевле под эти нужды поднимать линуховые тачки. Но, допустим, у вас веб-приложение на ASP.NET или веб-сервис, который хостится под IIS. Кэш вы храните как синглтон в рабочем процессе, у вас отлично реазилованы многопоточные блокировки и т.д. Или даже вы используете какую-нибудь библиотеку для кэширования. Однако, по расписанию регулярно происходит рестарт рабочего процесса, и весь кэш теряется - приходится грузить данные заново.

Еще хуже, если вы включаете для пула приложений режим "веб-сад", т.е. позволяете IIS запускать несколько рабочих процессов для вашего сервиса. Теперь каждый экземпляр вашего сервиса будет делать запросы в базу и складывать все в свой собственный кэш. Вот для таких случаев лучше всего подходит memcached для windows. Этот кэш будет существовать в единственном экземпляре, обслуживать сколько угодно процессов и сбрасываться только при рестарте системы.

Собственно, сам memcached тянуть отсюда (ищите бинарник под свою винду). В архиве будет один exe-шник. Его можно скопировать, например, в C:\Program files\Memcached\ (папку предварительно надо создать). Потом из командной строки запускаем memcached -d install, в результате он регистрирует себя как windows-сервис (службу) и выполняется с правами "локальная система". По умолчанию он слушает на 11211 порту. Осталось только стартануть сервис memcached из консоли управления службами. После перезагрузки он будет запускаться автоматически.

Теперь про клиентскую часть. Под .NET клиентов несколько, я себе поставил этот. Из архива нам нужны только 2 модуля: log4net.dll и собственно Enyim.Caching.dll. В проекте не забудьте указать ссылки на оба.

sample.config содержит фрагменты, которые вы должны вставить в app.config или web.config. Там может быть указано несколько серверов - вам надо оставить только один с адресом 127.0.0.1 и прописать порт 11211. Особое внимание на тэг <memcached enabled="true"> - так работать не будет. Его надо заменить просто на <memcached>.

Все, можно пользоваться. Мануалов к этому клиенту я не нашел, но он должен придерживаться общепринятого стандарта, так что пойдет любая документация по memcached-клиенту. Еще хочется отметить, что все собственные типы данных, которые вы собираетесь класть к кэш, должны быть помечены атрибутом [Serializable] или [DataContract] (это из WCF). В общем, они должны быть сериализуемыми.

Небольшой пример на C#:

// создание клиента очень быстрое,
// так что не обязательно делать его синглтоном

MemcachedClient client = new MemcachedClient();
// сохранить в кэше
client.Store(StoreMode.Set, "MyIntValue", 12345,
    DateTime.Now.AddMinutes(20));
// получить из кэша
int value = client.Get<int>("MyIntValue");
// удалить из кэша
client.Remove("MyIntValue");