I haven't tried this, but it appears you can use this MSBuild task to accomplish the same thing as ConstraintChanger. You have to include a copy of the DelegateConstraint.cs and IEnumConstraint.cs code files in your project. After the build task is applied to the project, the constraints are swapped out, and your other projects which reference this project will be able to see the constraints.
So its essentially useful for creating a common library project within your solution, and can include things like your own custom generic extension methods which are type constrained to System.Enum and System.Delegate.
https://code.google.com/p/unconstrained-melody/issues/detail?id=13
All credit to: j...@friesen.us
I've found this project to be useful but wanted to have the build-time steps in MSBuild. Adding this to your *.csproj files in which you use the constraints should accomplish the same thing as the Constraintchanger app. Note the following:
1. I've added the two constraint types to my classes root namespace and the substitution looks for the types in the assembly's root namespace.
2. To make Resharper happier I've added the IEnumConstraint, DelegateConstraint type args into my project inside an #if UNCONSTRAINED ... #endif block like so:
public static T Parse<T>(string val) where T : struct
#if UNCONSTRAINED
, IEnumConstraint
#endif
{
}
This is purely optional but keeps resharper from complaining about the constraint not matching when using the project's code from another project in a common solution.
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
SwapConstraints
</BuildDependsOn>
</PropertyGroup>
<ItemGroup>
<PreprocessorDefines Include="UNCONSTRAINED" />
</ItemGroup>
<UsingTask TaskName="FileReplace" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<FileName ParameterType="System.String" Required="true" />
<Source ParameterType="System.String" Required="true" />
<Replacement ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs"><![CDATA[
string content = File.ReadAllText(FileName);
content = content.Replace(Source, Replacement);
File.WriteAllText(FileName, content);
]]></Code>
</Task>
</UsingTask>
<Target Name="SwapConstraints">
<GetFrameworkPath>
<Output TaskParameter="Path" PropertyName="FW" />
</GetFrameworkPath>
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="SDK" />
</GetFrameworkSdkPath>
<PropertyGroup>
<ILDASM>"$(SDK)bin\NETFX 4.0 Tools\ildasm.exe"</ILDASM>
<ILASM>"$(FW)\ilasm.exe"</ILASM>
<IlFile>$(OutputPath)$(AssemblyName).il</IlFile>
<DllFile>$(OutputPath)$(AssemblyName).dll</DllFile>
</PropertyGroup>
<Exec Command="$(ILDASM) /OUT=$(IlFile) $(DllFile)" WorkingDirectory="$(ProjectDir)" />
<FileReplace FileName="$(IlFile)" Source="($(RootNamespace).DelegateConstraint)" Replacement="([mscorlib]System.Delegate)" />
<FileReplace FileName="$(IlFile)" Source="([mscorlib]System.ValueType, $(RootNamespace).IEnumConstraint)" Replacement="([mscorlib]System.Enum)" />
<FileReplace FileName="$(IlFile)" Source="($(RootNamespace).IEnumConstraint), [mscorlib]System.ValueType" Replacement="([mscorlib]System.Enum)" />
<FileReplace FileName="$(IlFile)" Source="($(RootNamespace).IEnumConstraint)" Replacement="([mscorlib]System.Enum)" />
<Exec Command="$(ILASM) /OUTPUT=$(DllFile) /DLL $(IlFile)" WorkingDirectory="$(ProjectDir)" />
</Target>
Nov 22, 2013
#1 j...@friesen.us
Sorry, didn't mean to say #if UNCONSTRAINED ... #endif should be around type args for DelegateConstraint. I've not messed with delegates to this point but I doubt that it would be necessary for them.