В одном проекте разбирал багу. Эта бага была связана с тем, что проект прошел некоторую техническую итерацию, т.е. бизнес логика в нём не поменялась, но в него была добавлена технология, которая неявно поменяла поведение кода, оставив его представление неизменным. И связано это именно с тем, что в некоторых случая class бывает не совсем class-ом.
Лабораторная по анализу этого бага приобрела форму задачки на знание технологии .NET.
Таким образом, прошу оценить формулировку задачки и её актуальность.
Ну, и если захотите, можете представить её решение. :)
Задачка
Есть код консольного приложения на c#:
#region Здесь код изменять нельзя
public sealed class ClassA
{
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
}
#endregion
#region Здесь код изменять нельзя
public sealed class ClassB
#endregion
#region Здесь код изменять нельзя
{
public void Do(ref ClassA a)
{
a.Value = 5;
}
}
#endregion
public class Program
{
public static void Main(string[] args)
{
#region Для класс Program изменять код только здесь
ClassB b = new ClassB();
#endregion
bool result = Process(b);
System.Console.WriteLine(result);
}
public static bool Process(ClassB b)
{
ClassA a1 = new ClassA();
ClassA a2 = a1;
b.Do(ref a2);
return a1.Value == a2.Value;
}
}
Запустив его на исполнение мы увидим, что метод Program.Process вернёт значение true и на консоль будет выведен текст «true».
Используя стандартный функционал .NET Framework (версией >= 2.0) допишите код для классов ClassA и ClassB, а также измените код в методе Program.Main, чтобы метод Program.Process вернул значение false и, соответственно, на консоль должно быть выведено «false».
Код в регионах #region Здесь код изменять нельзя менять нельзя. Для класса Program код можно поменять только в регионе region Для класс Program изменять код только здесь.
UPD Спасибо @DreamChild
Директивы условной компиляции C# не применять.
UPD
Хотел бы ещё раз уточнить, что нужно только дописать ClassA и ClassB, т.е. не нужно создавать новые классы.
UPD Ответ дал @hazzik
Суть в том, что выполнение функционал класса ClassB перевели в другой домен. Но при этом неявно поменялась логика работы метода ClassB.Do(ref ClassA) и экземпляр типа ClassA уже передаётся не как ссылочный объект, а как значимый объект, т.е. в результате сериализации получается структура значений полей класса ClassA, которая передается через границу домена. При выходе из метода ClassB.Do(ref ClassA) возвращается структура полей класс ClassA и, самое интересное, эта структура десериализуется в новый объект, ссылка на который и записывается в переменную a2. Таким образом, в a1 и в a2 получаются ссылки на два разных объекта, при чём, значения их внутренних полей различаются.
Ответ
То же самое, что и предыдущий мой ответ, только все это делаем средствами .NET fx, т.е. используем его прокси и, соответственно, все вызываем из отдельного домена:
[Serializable]
#region Здесь код изменять нельзя
public sealed class ClassA
{
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
}
#endregion
#region Здесь код изменять нельзя
public sealed class ClassB
#endregion
: MarshalByRefObject
#region Здесь код изменять нельзя
{
public void Do(ref ClassA a)
{
a.Value = 5;
}
}
#endregion
public class Program
{
public static void Main(string[] args)
{
#region Для класс Program изменять код только здесь
ClassB b = (ClassB) AppDomain.CreateDomain("X").CreateInstanceFromAndUnwrap(typeof(ClassB).Assembly.CodeBase, typeof(ClassB).FullName);
#endregion
bool result = Process(b);
Console.WriteLine(result);
}
public static bool Process(ClassB b)
{
ClassA a1 = new ClassA();
ClassA a2 = a1;
b.Do(ref a2);
return a1.Value == a2.Value;
}
}
Почему так происходит? ClassA не помечен для маршаллинга через границу доменов с передачей по ссылке (то, что делает MarshalByRefObject), по-этому он будет сериализован до передачи в домен "X" и в домене "X" десериализован в новый объект. Для внешнего домена никто никогда не изменял a1 его Value так и останется 0. При возвращении из домена "X" экземпляр класса ClassA будет сериализован и десериализован уже в обратном направлении и присвоен переменной a2
Если у параметра a метода ClassBDo убрать модификатор ref то картина будет немного иной: обратной десериализации из "X" в текущий домен происходить не будет, и, следовательно, a2 будет все-еще ссылаться на тот же объект, что и a1, а значение свойства Value этого объекта останется равным 0
Чтобы исправить эти "проблемы" нужно унаследовать ClassA от MarshalByRefObject
Комментариев нет:
Отправить комментарий