13 ноября 2008

Switcher - Аналог Expose под Windows Vista

В скором будущем, когда позволит финансовая ситуация, я стану свитчером - перейду на Mac OS X. Сейчас же денег нет и я пытаюсь сформировать макоподобный интерфейс в Windows Vista. 

И вроде почти ни с чем проблем не было - кроме так называемого Expose. Но после некоторого времени, проведенного в гугле и отбрасыванием бесполезных/умерших утилит я наткунлся на программу под приятным мне названием - Switcher

По функционалу - тот же Expose но еще более настраиваемый. Что не менее отрадно - полностью бесплатно. Позволяет активировать функцию из угла экрана либо по сочетанию клавиш(можно даже оверрайдить Win+Tab). Единственное требование Windows Vista с работающим Aero.

04 ноября 2008

Новые возможности C# 4.0. Часть 3: Ковариантность обобщений

Когда обобщения(generic) пришли к нам вместе с C# 2.0, они стали одной из лучших возможностей в этом языке. Те, кто когда-либо создавал классы строготипизированных коллекций в C# 1.0 знает, насколько они упростили нам жизнь и уменьшили количество кода. Единственная проблема заключалась в том, что обобщенные типы не следовали тем же правилам наследования, которые были в силе для обычных типов. Начнем, пожалуй с определения двух простых классов, которые мы будем использовать в этой статье:

   1:  public class Shape
   2:  {
   3:  }
   4:   
   5:  public class Circle : Shape
   6:  {
   7:  }

Это классическая иерархия классов, которая пока ничего конкретного не делает, но это нам сейчас и не нужно. Теперь определим dummy-класс, который может быть контейнером для любого типа.

   1:  public interface IContainer<T>
   2:  {
   3:      T GetItem();
   4:  }
   5:   
   6:  public class Container<T>: IContainer<T> 
   7:  {
   8:      private T item;
   9:   
  10:      public Container(T item)
  11:      {
  12:          this.item = item;
  13:      }
  14:   
  15:      public T GetItem()
  16:      {
  17:          return item;
  18:      }
  19:  }

У нас есть иерархия и контейнер. Теперь посмотрим на то, что мы сейчас не можем делать в текущей версии C# - 3.0.

   1:  static void Main(string[] args)
   2:  {            
   3:      IContainer<Shape> list = GetList();
   4:  }
   5:   
   6:  public static IContainer<Shape> GetList()
   7:  {
   8:      return new Container<Circle>(new Circle());
   9:  }

У нас есть метод GetList, у которого тип возращаемого значения определен как IContainer<Shape>, а возвращает он Container<Circle>. Так как Circle наследуется от Shape, а Container реализует интерфейс IContainer может показаться, что это сработает. Но, как вы догадались, C# 3.0 на это не способен.

В C# 4.0 у нас есть путь заставить это работать - нам нужно просто добавить ключевое слово out к параметру-типу в нашем определении интерфейса IContainer (замечу, что ковариантность в C# 4.0 ограничена интерфейсами и делегатам).

   1:  public interface IContainer<out T>
   2:  {
   3:      T GetItem();
   4:  }

Данная конструкция говорит компилятору, что тип T ковариантен, что означет то, что любой IContainer<T> будет принимать любой тип эквивалентный или более конкретный, чем T. Как мы видели выше, типом возвращаемого значения был IContainer<Shape>, но если мы поставим параметр out к нашему интерфейсу, то мы легко сможем вернуть и IContainer<Circle>.

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

   1:  public interface IContainer<out T>
   2:  {
   3:      void SetItem(T item);
   4:      T GetItem();
   5:  }

Но почему это не будет работать? Ответ на самом деле очень прост - безопасность типов. Давайте посмотрим на последствия того, что мы сделали:

   1:  static void Main(string[] args)
   2:  {
   3:      IContainer<Shape> container = new Container<Circle>();
   4:      SetItem(container);
   5:  }
   6:   
   7:  public static void SetItem(IContainer<Shape> container)
   8:  {
   9:      container.SetItem(new Square()); // BOOM!!!
  10:  }

Так как T ковариантен и, поэтому, мы можем присвоить Container<Circle> к переменной типа IContainer<Shape>, передав его дальше в наш статический метод SetItem, который принимает параметр типа IContainer<Shape> и, затем мы берем этот парметр и пытаемся добавить переменную типа Square в него. Кажется, что все верно - тип параметра IContainer<Shape> и это дает нам право добавить в него Square. Неверно. Это выражение "взорвется", так как мы пытаемся добавить Square в контейнер, который держит Circle. Поэтому, ключевым словом out, они ограничили ковариантность только одним направлением.

Вам, наверное, интересно как это все реализовано в CLR 4.0? В реализации нет нужды. Обобщенные типы работали в CLR 2.0 и они уже позволяли подобное поведение. Так как C# пытался сохранить безопасность типов - нам такое не дозволялось, но CLR справляется с этим на раз. И, небольшая ремарка - массивы в C# позволяют такое поведение, так что попробуйте. Я надеюсь, что вам понравилась эта статья и новая в этой серии не за горами!

Перевод статьи C# 4.0 New Features Part 3 - Generic Covariance

03 ноября 2008

Xbox 360 NXE: Второе рождение Xbox 360

19 ноября Microsoft выпустит обновление для интерфейса игровой приставки Xbox 360 - NXE. Это действительно крупное обновление, которое будет включать в себя следующие изменения:

Обновленный интерфейс

Переработана группировка меню - больше не придется искать куда же положился ваш свежескачанный ролик или демоверсия. Кроссылки в интерфейсе, например переходы от описания в новости, непосредственно к покупке.

Визуальное представление также улучшено - интерфейс теперь несколько напоминает CoverFlow из iTunes. И, естественно, он стал более красивым.

Установка игр на жесткий диск

Благодаря этому изменению происходит существенное ускорение загрузки игр. Многие игры, например GTA IV, избавляются от лагов при загрузке текстур в тяжелых местах. Уменьшается шум DVD-привода - чтение идет не с него, а с жесткого диска. Однако диск в приводе держать все же придется - еще одна защита от пиратов.

Аватар пользователя

Это человечек, которого будут видеть ваши друзья в своем в списке людей - идеологически полный аналог аватаров Wii Mii. Полностью кастомизируемый внешний вид - габариты, сложение, лицо, одежда - этакий Sims.

И, напоследок, несколько видео.

Сравнение загрузки GTA IV с жесткого диска и с DVD-диска

Приветствие

Аватары

ASP.NET MVC + jQuery = рай для AJAX

Я никогда раньше не имел дело ни с одним AJAX Toolkit от Microsoft, но недавно у меня появилась необходимость добавить картографический функционал к проекту, который я веду. Нам нужно было, чтобы у пользователей появилась возможность перемещать маркер по карте, а у нас получать его новые координаты на сервере. Очевидно, что нам для этого придется использовать AJAX в каком-либо виде. Сегодня я вам покажу насколько просто использовать связку ASP.NET MVC на сервере и jQuery на клиенте. Так как jQuery теперь включен в поставку ASP.NET MVC, то больше нет оправдания его не использовать.

Возьмем очень простой пример. У нас есть страница на которой я хочу выводить список людей, когда я нажимаю кнопку "Get people" и добавлять нового человека в базу данных, вводя его имя и нажимая "Add person". Вот как это выглядит:

Для начала создадим кнопку "Get people" и список, в который будем загружать людей:

   1:  <input type="button" id="getPeople" value="Get People" />
   2:  <ul id="people_list">

Далее напишем обработчик события "страница загружена", который устанавливает обработчик клика по кнопке.

   1:  $(function() {
   2:      $("#getPeople").click(function() {
   3:          $.getJSON("/Home/People", null, getPeople);
   4:      });
   5:  });

Когда кнопка нажата мы инициализирум запрос JSON данных с пути /Home/People, который мапится на метод People контроллера Home.

   1:  [AcceptVerbs(HttpVerbs.Get)]
   2:  public ActionResult People()
   3:  {
   4:      var db = new DataClasses1DataContext();
   5:      return Json(db.Persons);
   6:  }

Все, что мы тут делаем - это получаем список людей из базы данных(с помощью LINQ to SQL) и возвращаем их в виде JSON. Когда ответ получен браузером вызывается функция getPeople, как у мы указали это в методе getJSON. Вот код функции getPeople:

   1:  function getPeople(people) {
   2:      $("#people_list").text("");
   3:      $.each(people, function(i) {
   4:          $("#people_list").append("<li>" + this.Name + "</li>");
   5:      });
   6:  }

Данные, полученые от сервера приходят в нее в виде параметра people. Все что нам нужно сделать - это итереровать через коллекцию и для каждого ее элемента добавлять тег <li> в наш список людей. Как видите, взаимодействие ASP.NET MVC и jQuery реализуется очень просто - никакой ручной конвертации данных из объектов C# в объекты JavaScript делать не пришлось.

А что насчет обновления данных? Не менее просто. Для начала нам понадобится немного HTML кода: поле ввода и кнопка.

   1:  <label>Name <input type="text" id="name" /></label><br />
   2:  <input type="button" id="addPerson" value="Add Person" />

И еще один обработчик нажатия кнопки.

   1:  $("#addPerson").click(function() {
   2:      var name = $("#name")[0].value;
   3:      if (name == "") {
   4:          alert("You must provide a name");
   5:          return;
   6:      }
   7:      var person = { Name: name };
   8:      $.post("/Home/People", person, null, "json");
   9:  });

На этот раз мы использовали полезную функцию post, чтобы отправить наш свежесозданный объект JSON по адресу /Home/People методом POST. Так как используется метод POST, то этот запрос будет пойман нашим перегруженым методом People с атрибутом AcceptVerbs[HttpVerbs.Post].

   1:  [AcceptVerbs(HttpVerbs.Post)]
   2:  public ActionResult People(string Name)
   3:  {
   4:      var db = new DataClasses1DataContext();
   5:      var person = new Person {Name = Name};
   6:      db.Persons.InsertOnSubmit(person);
   7:      db.SubmitChanges();
   8:      return null;
   9:  }

Все что нам приходится сделать на сервере, чтобы получить данные из запроса это назвать параметр этого метода также, как и поле нашего JSON объекта, то есть Name. ASP.NET MVC автоматически найдет соответсвие и отдаст нам нужные данные. Если вам нужно использовать сложный JSON объект, то вы можете десериализовать его, используя встроеный databinding. Осталось только создать объект для базы данных и сохранить его.

Я поздно занялся Ajax, но со связкой ASP.NET MVC + jQuery добавление такого функционала так же легко и приятно, как дуновение свежего ветра.

Демо-Solution вы можете скачать здесь: http://static.mikehadlow.com/JQueryAjax.zip

Перевод статьи MVC Framework and jQuery = Ajax heaven

02 ноября 2008

Новые возможности C# 4.0. Часть 2: параметры по умолчанию

Сегодня мы поговорим о другой новинке C# 4.0, которую я ждал много лет. В прошлом, ее отсутствие объяснялось архитектурным решением. Но, видимо, прагматизм победил и теперь у нас есть параметры по умолчанию. Чтобы сделать их еще более полезными они добавили к ним именованые параметры. Мы обсудим их через пару минут, а сейчас займемся параметрами по умолчанию.

Предположим, что у нас есть такой класс:

   1:  public class TestClass
   2:  {
   3:      public void PerformOperation(string val1, int val2, double val3)
   4:      {
   5:          Console.WriteLine("{0},{1},{2}", val1, val2, val3);
   6:      }
   7:  }

Теперь мы можем инстанциировать его и вызвать метод:

   1:  var testClass = new TestClass();
   2:  testClass.PerformOperation("val", 10, 12.2);

Но, что, если мы знаем "хорошие" значения параметров, которые используются чаще всего? До сего дня решением являлась перегрузка методов:

   1:  public class TestClass
   2:  {
   3:      public void PerformOperation()
   4:      {
   5:          PerformOperation("val", 10, 12.2);
   6:      }
   7:      
   8:      public void PerformOperation(string val1)
   9:      {
  10:          PerformOperation(val1, 10, 12.2);
  11:      }
  12:   
  13:      public void PerformOperation(string val1, int val2)
  14:      {
  15:          PerformOperation(val1, val2, 12.2);
  16:      }
  17:   
  18:      public void PerformOperation(string val1, int val2, double val3)
  19:      {
  20:          Console.WriteLine("{0},{1},{2}", val1, val2, val3);
  21:      }
  22:  }

Довольно длинно. Но C# 4.0 дает нам лучшее решение:

   1:  public class TestClass
   2:  {
   3:      public void PerformOperation(string val1 = "val", int val2 = 10, double val3 = 12.2)
   4:      {
   5:          Console.WriteLine("{0},{1},{2}", val1, val2, val3);
   6:      }
   7:  }

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

   1:  var testClass = new TestClass();
   2:  testClass.PerformOperation("val", 10);

Очень хорошо. Третий параметр в этом вызове приравнен к 12.2 по умолчанию. Теперь VB.NET разработчики перестанут смеятся над нами. Более того, параметры по умолчанию распространяются и на конструкторы:

   1:  public class TestClass
   2:  {
   3:      public TestClass(string someValue = "testValue")
   4:      {
   5:      }
   6:   
   7:      public void PerformOperation(string val1 = "val", int val2 = 10, double val3 = 12.2)
   8:      {
   9:          Console.WriteLine("{0},{1},{2}", val1, val2, val3);
  10:      }
  11:  }

Больше никаких многократных перегрузок конструкторов!

А что случится, если мы опустим значение val2 в вызове метода, показаном выше? Тоесть мы хотим указать val1 и val3, но оставить значение по умолчанию для val2. Мы не можем сделать это так:

   1:  var testClass = new TestClass();
   2:  testClass.PerformOperation("val", 10.2);

Этот код не скомпилируется, так как 10.2 не может быть приведено к int - здесь C# пытается оставить по умолчанию третий параметр, а не второй, как нам нужно. Итак, какой выход у нас есть? Мы можем использовать именованые параметры. Они состоят из указания имени параметра, двоеточия и значения, которое мы передаем. Тоесть вызов будет выглядеть так:

   1:  var testClass = new TestClass();
   2:  testClass.PerformOperation("val", val3: 10.2);

Довольно аккуратно, но меня смущает тот факт, что теперь смена имени параметра будет нести такие кардинальные последствия. Я думаю только время покажет насколько это удобно в разработке больших приложений. Хотя, люди, работающие с другими языками живут с этим уже много лет.

Вот вам еще одна интересная новая возможность C# 4.0 и еще один повод приглядываться к новой VS2010.

Перевод статьи: C# 4.0 New Features Part 2 - default and named parameters

01 ноября 2008

Новые возможности C# 4.0. Часть 1: dynamic

Одна из самых интересных возможностей язык C# 4.0, который был представлен на PDC является новое ключевое слово - dynamic. Оно позволяет разработчику объявить объект, привязка к методам которого, будет осуществлятся на этапе выполнения, а не компиляции. Примечательно, что класс, который инстанциирует этот объект объявляется стандартным способом.

   1:  public class TestClass
   2:  {
   3:      public void TestMethod1()
   4:      {
   5:          Console.WriteLine("test method 1");
   6:      }
   7:   
   8:      public void TestMethod2()
   9:      {
  10:          Console.WriteLine("test method 2");
  11:      }        
  12:  }

Теперь мы можем вызывать его методы как обычно:

   1:  var test = new TestClass();
   2:  test.TestMethod1();
   3:  test.TestMethod2();

Как мы и ожидали все прекрасно скомпилировалось. Теперь воспользуемся новым ключевым словом dynamic:

   1:  dynamic test = new TestClass();
   2:  test.TestMethod1();
   3:  test.TestMethod2();

Ничего не изменилось? Отнюдь. Все точно так же компилируется, как и раньше, но теперь вызовы методов будут привязываться на этапе выполнения, а не компиляции. Тоесть, если мы напишем вот так:

   1:  dynamic test = new TestClass();
   2:  test.TestMethod1();
   3:  test.TestMethod2();
   4:  test.TestMethod3();

Код все-равно успешно скомпилируется, но после запуска мы получим данное исключение:

Что же тут произошло? Вот что нам подсказал Reflector:

   1:  private static void Main(string[] args)
   2:  {
   3:      object test = new TestClass();
   4:      if (<Main>o__SiteContainer0.<>p__Site1 == null)
   5:      {
   6:          <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "TestMethod1", typeof(object), null));
   7:      }
   8:      <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, test);
   9:      if (<Main>o__SiteContainer0.<>p__Site2 == null)
  10:      {
  11:          <Main>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "TestMethod2", typeof(object), null));
  12:      }
  13:      <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, test);
  14:  }

Для начала компилятор сгенерировал поле o__SiteContainer0, для хранения информации о месте вызова. Если вы посмотрите на переменную test, которая содержит наш объект, то увидите, что ее тип стал типом object. Так что, в реальности, это не динамический тип, а всего-лишь помощник со стороны компилятора.

Дальше идет проверка мест вызова(callsite) на null и если они ему равны, то используется CallSite.Create, в который передается объект CSharpCallPayload, который содержит всю информацию о методе, который мы вызываем. Как только место вызова указано, то оно просто вызывается у нашей переменной test. Как видите - компилятор все сделал за нас.

В данном примере то что мы делаем кажется чертовски бесполезным, но мощь этой возможности проявляется тогда, когда мы используем тип, методы которого мы не знаем на этапе компиляции. Это может прийти от динамических языков(например IronRuby), если это сгенерированный класс, или что угодно из того, для чего мы используем Reflection.

Ремарка: я принял рещение исключить из перевода часть про тестирование производительности, так как в статье C# 4.0 New Features Part 1.1 - dynamic keyword second look автор провел новое тестирование и оно показало заметно лучшие результаты.

Перевод статьи C# 4.0 New Features Part 1 - dynamic keyword

31 октября 2008

LINQ to SQL: обновление модели из базы данных

Если вы используете LINQ to SQL в паре с его дизайнером вы наверняка в курсе его неприятного ограничения - невозможно обновить модель из базы данных.

Как ни странно с того момента, как я последний раз изучал эту ситуацию - решение появилось:

Huagati DBML/EDMX Tools для Visual Studio 2008 / .net 3.5

Вот в кратце возможности:

  • LINQ to SQL
    • Стандартизация имен классов и членов (удаление префиксов tbl_, int_, str_, приведение регистра символов)
    • Обновление диаграмы дизайнера из базы данных
    • Обновление документации из базы данных
    • Сравнение диаграмы дизайнера и базы данных
  • ADO.NET Entity Framework
    • Стандартизация имен классов и членов (удаление префиксов tbl_, int_, str_, приведение регистра символов)
UPDATE: к сожалению, выяснилось, что данная утилита платная - будем искать лекарство

Unit Testing. Что это и чем это нам грозит?

Благодаря тлетворному влиянию прекрасного разработчика, моего коллеги, Никиты Говорова я начал изучать Best Practices. Первой из этих практик естественно стало модульное тестирование, к тому же в тот момент подоспел очередной preview ASP.NET MVC, а в месте с ним серия скринкастов MVC Storefront, с которыми я рекомендую ознакомиться, даже если вы не связаны с веб-разработкой.

Итак, модульное тестирование. Концепцию удобнее показать на примере.

Предположим, что перед нами стоит простая задача - написать целочисленный калькулятор. Как ни трудно догадаться основой его будет класс Calculator, который мы с вами и будем писать. Для удобства создадим интерфейс ICalculator, который покажет какие методы нам нужны:

public interface ICalculator
{
   int Add(int first, int second);
   int Substract(int first, int second);
   int Multiply(int first, int second);
   int Divide(int first, int second);
}

Далее нам нужно реализовать этот интерфейс в классе Calculator. Мы будем делать это поэтапно для каждого необходимого нам метода.

Этап 1: Написание теста

Да, как это парадоксально не звучит - сначала пишется тест, потом то, что тестируется. Это основное "неудобство" данной практики для новичков, так как очень сложно заставить писать тест еще до того, как написан сам код.

Для написания теста нам нужно добавить в наш Solution новый проект типа Test Project(в данной статье мы будем использовать встроенные тесты Visual Studio 2008).После его создания у нас появится класс UnitTest1 с атрибутом [TestClass], содержащий в себе метод TestMethod1 с атрибутом [TestMethod].

Для начала переименуем метод во что-нибудь более удобоваримое, например в Calculator_Add_MustAddFirstIntToSecondAndReturnTheResult(). Не пугайтесь такого длинного названия - вызывать напрямую этот метод вам нигде не придется, но это добавит ясность к тесту в том случае, когда их у вас в проекте станет несколько сотен. Основная идея - назвать метод так, чтобы по названию стало ясно, что он делает(тестирует).

Небольшая ремарка: Мой коллега Никита Говоров недавно предложил воспользоваться возможностью C# и писать название тестовых методов на русском языке. Это спорное с моей точки зрения решение, так как может вызвать труднонаходимые опечатки из-за переключения раскладки, но если у вас есть сложности с английским языком, то попробуйте - возможно это то, что вам нужно.

Теперь собственно напишем сам тест, который будет тестировать работу метода Add.

[TestMethod]
public void Calculator_Add_MustAddFirstIntToSecondAndReturnTheResult()
{
   Calculator calculator = new Calculator();

   int result = calculator.Add(1, 2);

   Assert.AreEqual(3, result);
}

Как видите - тут все просто. Мы создаем экземпляр класса Calculator, и вызываем у него метод Add, записывая результат. Последняя инструкция - собственно проверка равен ли первый аргумент(тот, результат, который мы ожидаем) второму(результат, который мы получили). Если это не так - метод Assert.AreEqual выбросит исключение. Тест считается пройденным(Passed), если не было выброшено ни одного исключения.

Этап 2: Провалим тест

На этом этапе мы должны убедится, что то что мы собираемся проверять - действительно это проверяет. Так как мы еще не реализовали сам метод Add запуск теста приведет к провалу(Failed), что нам и нужно. Запуск теста осуществляется кликом по телу метода и вызовом команды контекстного меню Run Tests.

Это может показаться излишним, но действительно довольно часто встречаются ситуации, когда тестовый проходит(Passed), хотя никаких условий этому не обеспечено. Например вы можете ошибиться и проверять вместо длины коллекции возвращенных объектов методом Assert.AreNotEqual(0, collection.Length) ее неравность null методом Assert.IsNotNull(collection). При количестве элементов равном нулю ваш тест пройдет, хотя должен быть Failed.

Этап 3: Реализуем метод

На этом этапе с одной стороны все просто, с другой - не очень. Необходимо написать минимальный функционал, благодаря которому тест пройдет. Для примера возьмем метод Divide. Мы уже написали для него тест на предыдущем этапе:

[TestMethod]
public void Calculator_Divide_MustDivideFirstIntBySecondAndReturnTheResult()
{
   Calculator calculator = new Calculator();

   int result = calculator.Divide(4, 2);

   Assert.AreEqual(2, result);
}

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

[TestMethod]
public void Calculator_Divide_MustCheckSecondArgumentOnZeroAndThrowExceptionIfTrue()
{
   Calculator calculator = new Calculator();

   bool catched = false;

   try
   {
       int result = calculator.Divide(4, 0);
   }
   catch (Exception ex)
   {
       Assert.IsInstanceOfType(ex, typeof (ArgumentException));
       catched = true;
   }
   Assert.IsTrue(catched, "No exception throwed - something wrong");
}

И только после этого реализовывайте соответствующий функционал.

Этап 4: Проходим тест

Последний этап самый простой - убедится что тест прошел.

Заключение

Как видите использование данной практики связано с большим расходом времени(нормальное соотношение строк кода к тестам - 1:2) и очень большой силой самоконтроля - непросто заставить себя идти ровно по шагам не забегая вперед тестов. Но - это того стоит. Во время написания тестов находится много потенциальных логических ошибок, которые в противном случае всплыли бы на этапе выполнения.

В этой статье мы использовали Unit Test Framework, который идет в комплекте с Visual Studio. В следующей статье мы рассмотрим альтернативный - MbUnit.

До новых встреч.

Стимул

Давным-давно по моему летоисчислению я начал вести этот блог. И даже смог из себя выдавить пару статей, одна из которых, даже помогла кому-то. Но, как и все мои другие начинания, мне стало лень/нет времени его вести.

И так бы ничего и не изменилось, если бы не одно событие, показавшее мне, что это все-таки не зря - вести блог. Ходя по форуму sql.ru в перерыве на работе я заметил топик про уже описаный мною продукт - UrlRewriting.NET. И, как это было для меня неожидано, увидел в нем ссылку, ведущую на блог, который вы сейчас читаете.

Вроде мелочь, скажете вы, и я с вами соглашусь. Но эта мелочь - тот стимул, которого мне не хватало.

Этот пост - всего-лишь вступление, попытка вспомнить клавиатуру и разговорный язык - на ближайшее время я наметил пару статей по юнит-тестированию в .NET и целом и в ASP.NET MVC в частности. Также, вероятно, мы с вами коснемся различных паттернов проектирования, как то Repository, IoC и, возможно, других, если я найду их для себя интересными и применимыми в своей реальной жизни.

До скорых встреч, коллеги!