Лично я не знаю каких-либо сторонних или предыдущих решений этой проблемы, поэтому приношу свои извинения, если я покрываю старые позиции. Но если бы я собирался реализовать какой-то стандарт неизменности для проекта, над которым я работал, я бы начал с чего-то вроде этого:
public interface ISnaphot<T>
{
T TakeSnapshot();
}
public class Immutable<T> where T : ISnaphot<T>
{
private readonly T _item;
public T Copy { get { return _item.TakeSnapshot(); } }
public Immutable(T item)
{
_item = item.TakeSnapshot();
}
}
Этот интерфейс был бы реализован примерно так:
public class Customer : ISnaphot<Customer>
{
public string Name { get; set; }
private List<string> _creditCardNumbers = new List<string>();
public List<string> CreditCardNumbers { get { return _creditCardNumbers; } set { _creditCardNumbers = value; } }
public Customer TakeSnapshot()
{
return new Customer() { Name = this.Name, CreditCardNumbers = new List<string>(this.CreditCardNumbers) };
}
}
И клиентский код будет выглядеть примерно так:
public void Example()
{
var myCustomer = new Customer() { Name = "Erik";}
var myImmutableCustomer = new Immutable<Customer>(myCustomer);
myCustomer.Name = null;
myCustomer.CreditCardNumbers = null;
//These guys do not throw exceptions
Console.WriteLine(myImmutableCustomer.Copy.Name.Length);
Console.WriteLine("Credit card count: " + myImmutableCustomer.Copy.CreditCardNumbers.Count);
}
Очевидный недостаток заключается в том, что реализация хороша только как клиент реализации ISnapshot
TakeSnapshot
, но на по крайней мере, это будет стандартизировать вещи, и вы будете знать, где искать, если у вас есть проблемы, связанные с сомнительной изменчивостью. Бремя также будет лежать на потенциальных разработчиках, чтобы понять, могут ли они обеспечить неизменность снимка и не реализовать интерфейс, если нет (то есть класс возвращает ссылку на поле, которое не поддерживает какой-либо вид клонирования / копирования и, следовательно, не может быть снимок-е изд.)
Я понимаю, что это не так уж далеко от простой реализации объектной копии, но она стандартизирует копию с точки зрения неизменности. В кодовой базе вы можете увидеть некоторых разработчиков из ICloneable
, некоторых конструкторов копирования и некоторые явные методы копирования, возможно, даже в одном классе. Определив что-то вроде этого, вы узнаете, что намерение определенно связано с неизменяемостью - я хочу сделать снимок, а не дублирующий объект, потому что мне нужно больше и этого объекта. Класс Immtuable<T>
также централизует связь между неизменяемостью и копиями; если позже вы захотите как-то оптимизировать, например, кэшировать снимок до «грязного», вам не нужно делать это во всех разработчиках логики копирования.
В поисках решения той же проблемы, с которой вы столкнулись, я пришел к предлагаемому решению, заключающемуся в наличии условия в ItemGroup. Но это имело побочный эффект, потому что в ссылках Visual Studio я мог видеть обе ссылки, что также повлияло на ReSharper.
Наконец, я использую «Выбрать, когда в противном случае», и у меня больше нет проблем с ReSharper и Visual Studio, показывающими две ссылки.
<Choose>
<When Condition=" '$(Configuration)' == 'client1DeployClickOnce' ">
<ItemGroup>
<ProjectReferenceInclude="..\client1\app.Controls\app.Controls.csproj">
<Project>{A7714633-66D7-4099-A255-5A911DB7BED8}</Project>
<Name>app.Controls %28Sources\client1\app.Controls%29</Name>
</ProjectReference>
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<ProjectReference Include="..\app.Controls\app.Controls.csproj">
<Project>{2E6D4065-E042-44B9-A569-FA1C36F1BDCE}</Project>
<Name>app.Controls %28Sources\app.Controls%29</Name>
</ProjectReference>
</ItemGroup>
</Otherwise>
</Choose>
Вы можете прочитать об этом больше в моем сообщении в блоге: ProjectReference with Condition в файле проекта MSBuild
Каждый элемент MSBuild ( нормально, почти каждый ) может иметь связанное с ним Условие . Я бы посоветовал вам отредактировать файл проекта (который сам является файлом MSBuild) и поместить все ссылки на SQL-сервер в ItemGroup, для которой есть условие, например:
<ItemGroup Condition="'$(SqlServerTargetEdition)'=='2005'">
<!-- SQL Server 2005 References here -->
<Reference Include="..."/>
</ItemGroup>
И еще одна ItemGroup для Sql server 2008:
<ItemGroup Condition="'$(SqlServerTargetEdition)'=='2008'">
<!-- SQL Server 2008 References here -->
<Reference Include="..."/>
</ItemGroup>
Вы должны предоставить значение по умолчанию для свойства SqlServerTargetEdition, прежде чем эти элементы будут объявлены. Затем в командной строке вы можете переопределить это значение с помощью переключателя / p при вызове msbuild.exe .
Саид Ибрагим Хашими
Моя книга: Внутри Microsoft Build Engine: Использование MSBuild и Team Foundation Build