Running with Code Like with scissors, only more dangerous

17Jan/080

My C# 4.0 Wishlist, Part 3: The Return of Const-ness

Posted by Rob

In C++, I can decorate member functions with the const modifier, which indicates that calling the member function will not modify the internal state of the object.  Here's a sample class definition:

Test.h:

   1:  class CTest
   2:  {
   3:  private:
   4:      int m_nVal;
   5:   
   6:  public:
   7:      CTest(void);
   8:      ~CTest(void);
   9:      int GetValue() const;
  10:      void SetValue(int value);
  11:      int Add(int value) const;
  12:  };

Test.cx:

   1:  #include "Test.h"
   2:   
   3:  CTest::CTest(void)
   4:  {
   5:  }
   6:   
   7:  CTest::~CTest(void)
   8:  {
   9:  }
  10:   
  11:  int CTest::Add(int value) const
  12:  {
  13:      return value + m_nVal;
  14:  }
  15:   
  16:  int CTest::GetValue() const
  17:  {
  18:      return m_nVal;
  19:  }
  20:   
  21:  void CTest::SetValue(int value) 
  22:  {
  23:      m_nVal = value;
  24:  }

This example demonstrates wrapping an integer value, and shows how GetValue() and Add() can be const by not modifying any internal values.  Now, if I change the Add method to a void type, and add the value to the internal state, I get a compiler error.  Here's the updated method:

   1:  void CTest::Add(int value) const
   2:  {
   3:      return SetValue(value + m_nVal);
   4:  }

Error:

error C2662: 'CTest::SetValue' : cannot convert 'this' pointer from 'const CTest' to 'CTest &'

I get a similar error (about lvalue type casting) if I just set the value within the Add method.

So how should this apply in C#?  Realistically, I think I'd like it to just apply to member functions and properties.  There are a lot of ways to use const in C and C++ - it's almost scary, actually (could you imagine using one parameter and having three const modifiers?).  In C#, I'd just like it to be part of the method contract:

   1:  public class Class1
   2:  {
   3:      private string m_firstName, m_lastName;
   4:      private int m_val;
   5:   
   6:      public int Value
   7:      {
   8:          get const
   9:          {
  10:              return m_val;
  11:          }
  12:          set
  13:          {
  14:              m_val = value;
  15:          }
  16:      }
  17:   
  18:      public string GetName() const
  19:      {
  20:          return string.Format("{0}, {1}", m_lastName, m_firstName);
  21:      }
  22:  }

In both of these examples, we can tell that the internal state of the object itself isn't modified (note that the const modifier only applies to the get method of the Value property).  It provides the user of the class additional information, and it helps to enforce the contract on the side of the class author.

Implementation in the compiler: add a System.Runtime.CompilerServices.ConstMethodAttribute and apply it to the methods as marked.  Add a static code analysis rule that checks to see if a method could be marked as const, and if so, flag a warning.

I don't know that there are compiler optimizations that can be made, but one way or another, I think that it's a good method with which to give additional information about method implementations.  Sometimes we don't want to call properties or methods if we know that it can cause side effects, because let's be honest: the base class library's documentation isn't always 100% clear.  That's why we need tools like .NET Reflector.  One more tool to help our code be self-documenting is one more good thing.

15Jan/080

My C# 4.0 Wishlist, Part 1 : Eliminate Type Constraint Constraints

Posted by Rob

C# 2.0 introduced a great new feature to the .NET Type system: generics.  Generics are really cool in that they allow you to define template classes; I can use a single class definition to provide a strongly typed collection, for example.  They enable some other tricks that I would tend to consider something of a "hack" as well; for example, this expression evaluates to true:

   1:  typeof(IEnumerable<int>) != typeof(IEnumerable<double>)

This expression is nice because there are some odd class design decisions in places like the PropertyGrid's type structure.  For example, in order to add a PropertyTab to the list of the PropertyGrid's tabs, you need to add a Type to the PropertyTabCollection exposed by the PropertyGrid's PropertyTabs property.  The PropertyGrid caches the tabs that it creates, and so you can't add a single Type to create two property tabs.  Consequently, even if you override the CreateTab method, you can't expect to add two tabs with the same Type.

My solution, then, was to create ExtensionPropertyTab<T>.  This class's type parameter is utterly useless; I create an arbitrary Type using Reflection Emit, close my ExtensionPropertyTab generic type definition with it, and then add the PropertyTab with that closed type.  Works great!  This stuff will be in an upcoming blog post about my PropertyGridEx project.

All of that is leading up to my next hack and, ultimately, my wishlist item #1 for C# 4.0.

There's a simple design-time class called EnumConverter.  This class is the default type converter for all enumeration types; EnumConverter is what displays the Enum names in the property grid when you're choosing items.  I'm creating a type surrogate class that allows you to customize the names of properties, and I've also been working on displaying better values for enumerations.  To this end, I created the EnumTypeConverter<T> class - this class provides enumeration names, but also retrieves friendly names from attributes on each enum entry.  Using generics, I'm able to cache the friendly names so that reflection only needs to be invoked once; System.Enum does something similar.

What I'd like to do, however, is say this:

   1:  public class EnumTypeConverter<T> : TypeConverter where T : enum

C# doesn't allow me to do this.  I get two errors:

error CS1031: Type expected

error CS1001: Identifier expected

So I try using the type name instead:

   1:  public class EnumTypeConverter<T> : TypeConverter where T : Enum

C# doesn't like this either:

error CS0702: Constraint cannot be special class 'System.Enum'

Why?  I get the same error with System.ValueType, even though ultimately it means the same thing as "struct" (though I could understand this difference).  But I can't do this with System.Delegate either (how about calling Invoke() or BeginInvoke() on T?).

Being able to specify Enum as a base would allow me to:

  • Explicitly cast between T and integral numeric types.
  • Specify 0 as the default value of T rather than using default(T).
  • Perform bitwise operations on them

There's really no reason to have a constraint like "Constraint cannot be special class 'System.Enum'."  Let's eliminate this artificial barrier - there shouldn't be any changes needed to be made to the CLR.

15Jan/080

My C# 4.0 Wishlist, Part 2 : Default/Optional Parameters

Posted by Rob

When I was first getting into C# (about .NET 1.0 Beta 2), I saw that it didn't support optional parameters.  The explanation was simple enough: method overloads supported an alternative method of default or optional parameters.  I thought that it was probably a useful choice.  But, check this out:

   1:  public static class MessageBox
   2:  {
   3:      static void Show(string message)
   4:      {
   5:          Show(message, null, MessageBoxButtons.OK, MessageBoxIcon.Information);
   6:      }
   7:   
   8:      static void Show(string message, string title)
   9:      {
  10:          Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information);
  11:      }
  12:   
  13:      static void Show(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
  14:      {
  15:          // actually perform the showing
  16:      }
  17:   
  18:      // and more overloads
  19:  }

This is pretty lame, isn't it?  Why can't I just do everything with a single method?

   1:  public static class MessageBox
   2:  {
   3:      static void Show(string message, string title = "", MessageBoxButtons button = MessageBoxButtons.OK, 
   4:          MessageBoxIcon icon = MessageBoxIcon.Information);
   5:  }

So, the question is, how precisely could this work?  Well, let's take a look at how this works in Visual Basic.

   1:  Public Class MessageBox
   2:      Public Shared Sub Show(ByVal message As String, Optional ByVal title As String = "", _
   3:                             Optional ByVal buttons As MessageBoxButtons = MessageBoxButtons.OK, _
   4:                             Optional ByVal icon As MessageBoxIcon = MessageBoxIcon.Information)
   5:      End Sub
   6:  End Class

Visual Basic turns this into a method and annotates the parameter list with attributes.  In C# we could express it like this:

   1:  public static class MessageBox
   2:  {
   3:      static void Show(string message, 
   4:          [Optional]
   5:          [DefaultParameterValue("")]
   6:          string title, 
   7:          [Optional]
   8:          [DefaultParameterValue(MessageBoxButtons.OK)]
   9:          MessageBoxButtons buttons, 
  10:          [Optional]
  11:          [DefaultParameterValue(MessageBoxIcon.Information)]
  12:          MessageBoxIcon icon)
  13:      {
  14:   
  15:      }
  16:  }

In languages that support optional parameters, the compiler provides parameters, so that a call that leaves off optional parameters looks (in IL) like a call that included the parameters.

I suggest that we use a compiler trick - dump the attributes and actually implement the overloads.  This has the awesome benefit of being entirely compiler-dependent and entirely backwards-compatible even to .NET 2.0.  We can even include one overload with the attributes included, so that development tools and compilers that use the attributes can tell the user about the optional parameter information, and the existing compilers can compile against a library using the new methods.

Consider this overload based on the above demonstrated Show method.

   1:  public static class MessageBox
   2:  {
   3:      static void Show(string message, MessageBoxIcon icon)
   4:      {
   5:          Show(message, "", MessageBoxButtons.OK, icon);
   6:      }
   7:  }

This function has the advantage of being inline-able, even if it means a slightly (and I mean ever-so-slightly) hit to file size.  I'm not saying it'd be good or effective to have 10 optional parameters - just that it wouldn't be bad to have a few.

Finally, my suggestion for the syntax of how this all should work out - use the default keyword for each item when you want to specify options:

   1:  void Go()
   2:  {
   3:      MessageBox.Show("This is a test.", default, MessageBoxButtons.OK);
   4:  }

CLI implementation provides an attribute on the actual implementing method so that we can figure out which is the actual default value - the compiler then has the option of whether to call substituting in the actual value (as it's implemented in VB now) or call the correct overload (which is what the current C# compiler would do).