четверг, 17 марта 2011 г.

Неожиданная проблема при использовании лямбда-выражений и шаблонов

В программе понадобилось создать несколько классов, позволяющих выбирать некоторый объект из коллекции. Так как все эти классы были как две капли воды похожи друг на друга, решил вытащить общий абстрактный класс примерно такой :
public abstract class SelectorBase<T>
{
public List<T> RegiteredObjects { get; private set; }
public virtual T SelectedObject { get; set; }
}

Затем по ходу развития программы некоторые методы выдавали исключительные ситуации ConnectionException, связанные с конектом к базе данных. Опять же, чтобы избежать многократного дублирования кода вытащил обработку исключительной ситуации в класс Selector.
public abstract class SelectorBase<T>
{
public bool HasProblem { get; protected set; }
public List<T> RegiteredObjects { get; private set; }
public virtual T SelectedObject { get; set; }
protected void NoExceptedAction(Action action)
{
try
{
action();
HasProblem = false;
}
catch(ConnectionException)
{
HasProblem = true;
}
}
public SelectorBase()
{
HasProblem = false;
RegiteredObjects = new List<T>();
}
}

Теперь для реализации простенького селектора, достаточно его просто наследовать от класса SelectorBase. Получается весьма лаконичный селектор, например такой
public class StringSelector : Selector<string>
{
public override string SelectedObject
{
get
{
return base.SelectedObject;
}
set
{
NoExceptedAction(
() =>
{
//здесь проверяем объекта value в базе данных
base.SelectedObject = value;
});
}
}
}
Все кажется достаточно безопасным, однако все селекторы оказались нерабочими даже в следующей маленькой программе
public class Program
{
public static void Main(string[] args)
{
var pv = new StringSelector();
pv.SelectedObject = "some string";
}
}

Программа вылетает с нечеловеческими матюгами, выдавая следующий Exception
System.BadImageFormatException 
was caught Message=Была сделана попытка загрузить программу, имеющую неверный формат. (Exception from HRESULT: 0x8007000B)
Source=MainConsole
StackTrace:
at MainConsole.StringSelector.<>c__DisplayClass1.<set_SelectedObject>b__0()
at MainConsole.Selector`1.NoExceptedAction(Action action) in <путь к файлу>


После получасовых поисков и капитального взрыва мозга удалось понять, что я напоролся на известную проблему компилятора C#. Чтобы мои селекторы заработали, пришлось немного изменить код
public class StringSelector : Selector<string>
{
public override string SelectedObject
{
get
{
return base.SelectedObject;
}
set
{
string res = null;
NoExceptedAction(
() =>
{
//здесь, например, проверяем объекта value в базе данных
//и наконец
res = value;
});
base.SelectedObject = res;
}
}
}

Комментариев нет:

Отправить комментарий