3

I just installed the nuget package for Jon Skeet's Unconstrained Melody project, but when I attempt to use it, I get an error when I compile:

Type parameter 'T' inherits conflicting constraints 'UnconstrainedMelody.IEnumConstraint' and 'System.ValueType'

Function definition:

public void SetEnum<T>() where T : struct, IEnumConstraint {}

Am I missing something? Should I not use the nuget package?

Bobson
  • 13,498
  • 5
  • 55
  • 80
  • 6
    I *really* wanted to create a `jon-skeet` tag for this question, but I held off... – Bobson May 02 '13 at 14:13
  • when reading the article i saw that T cannot be System.ValueType, how can the constraint be conflicting then??^^ – Vogel612 May 02 '13 at 14:17
  • Last release for that project on google was 2009... maybe newer versions of c# broke it? – Nathan May 02 '13 at 14:19
  • Looks like a bug in the compiler. – oleksii May 02 '13 at 14:19
  • @Nathan - It was updated for VS2010 in 11/2010, and the Nuget package is from 8/2011. ([Changelog](http://code.google.com/p/unconstrained-melody/source/list)). So it should work. – Bobson May 02 '13 at 14:21
  • Please log as an issue here: https://code.google.com/p/unconstrained-melody/issues – Matt Johnson-Pint May 02 '13 at 14:21
  • @MattJohnson - I'll do that if no one has run into this before, but it seems like such a basic usecase that I can't believe that it's actually a bug and not something I just failed to do... – Bobson May 02 '13 at 14:23
  • @Bobson An "Unconstrained Melody" tag might be valid however, and I'm sure he would then track that tag. – Adam Houldsworth May 02 '13 at 14:54

3 Answers3

2

I could be wrong, but it appears that while this library uses IEnumConstraint internally, and gets it to work with the postbuild steps described in the article, it does not provide any magic for you to just consume IEnumConstraint directly for your own methods.

The GetValues<T> method described in the post is one of several methods provided from the UnconstrainedMelody.Enums class. There are other objects and methods available as well.

If you wanted to constrain your own generic methods to enums, you could follow the same steps Jon used to build this library, but on your own library. There was also this example in the comments of how to do this with PostSharp.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Interesting. I think you might be right. That would explain the `but if you want to use the "constraint changer" you'll need to download and build the source from this site.` line on the google code project page. If so, I definitely misunderstood the purpose of the library. – Bobson May 02 '13 at 15:01
  • @Bobson - I wasn't aware of this library until you pointed it out, and I came to the same conclusion initially. The Nuget package description is more accurate. The blog post is more about how it works, than what it does. – Matt Johnson-Pint May 02 '13 at 15:07
2

The ideas of the "unconstrained melody" have been integrated into

It comes with the necessary tooling to incorporate the IL weaving into the build process and is being actively maintained. Therefore, it should be preferrable to use this over the code from "unconstrained melody". For Enums, Enums.NET also offers additional improvements.

ExtraConstraints also supports adding constraints to delegates.

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
0

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.
Casey Plummer
  • 2,629
  • 23
  • 20