This Saturday from 2:45-4:00 I’ll be giving a talk on “What’s coming up in VS2010 for C# Developers?” at Seattle Code Camp. I’ll mostly be showing off some of the new features we’ve been working on for VS2010, especially those related to the C# editor. I’d love it if you stopped by!
In VS2008 SP1 our team added a significant new feature: live semantic analysis of open files (or “background squiggles” as we called it internally). Unfortunately, soon after release we found a few small issues with the release. They are:
We spent some time analyzing the issues, and we’ve come up with a hotfix to address them. If you’re seeing any issues where you get live semantic errors that you don’t expect, please download this fix and try it out.
Let me know if you find any issues with it!
Thanks!
Yesterday at the PDC, Dustin announced the availability of CodeRush Xpress for C#. This is a free addin for VS 2008 that provides a whole bunch of fantastic editor productivity features. Download it today and give it a shot!
The topic of removing elements from a dictionary came up recently on an internal mailing list. Someone was iterating through all the elements of a collection and wanting to remove some of them. The problem with that is that modifying the collection underlying the enumerator throws. He had the following pseudo code:
void RemoveSomeElements(Dictionary<string, int> dict) { foreach (var e in dict) { if (e.Value < 35) { // Remove e! } } }
Several people replied that he could just create a list of elements to be removed and then iterate that afterwards:
var keysToRemove = new List<string>(); foreach (var e in dict) { if (e.Value < 35) { keysToRemove.Add(e.Key); } } foreach (var key in keysToRemove) { dict.Remove(key); }
That works, but it’s pretty verbose. Someone suggested a more functional way to do it that’s pretty nice:
dict.ToList().Where(pair => pair.Value < 35).ToList().ForEach(pair => dict.Remove(pair.Key));
At this point I decided to show off, and make it more efficient by using an “Apply” extension method instead of the .ForEach method that shipped on List<T> in .Net 2.0. I came up with this:
dict.Where(pair => pair.Value < 35).Apply(pair => dict.Remove(pair.Key)).Apply();
Along with the code to Apply:
public static class ApplyExtensions { public static IEnumerable<T> Apply<T>(this IEnumerable<T> source, Action<T> action) { Argument.NotNull(source, () => () => source); Argument.NotNull(action, () => () => action); return ApplyInternal(source, action); } internal static IEnumerable<T> ApplyInternal<T>(IEnumerable<T> source, Action<T> action) { foreach (var e in source) { action(e); yield return e; } } public static void Apply<T>(this IEnumerable<T> source) { foreach (var e in source) { // do nothing, just make sure the elements are enumerated. } } }
Here I thought I was doing right by C# 3.0, and showing people how to use lazily chained iterators. Unfortunately, this code drops us right back to the original problem! Because of lazy iteration, we’re actually still iterating the dictionary when we try to remove the elements, and so we invalidate the first iterator in our chain.
It’s a simple fix, but still a reminder that the semantics of delayed execution can be tricky to reason about sometimes:
dict.Where(pair => pair.Value < 35).ToArray().Apply(pair => dict.Remove(pair.Key)).Apply();
The “ToArray” call finishes the iteration of the dictionary, but uses less space than the original “.ForEach” solution, since we only realize a collection of the elements to be removed, instead of all of the elements in the dictionary.
I hate using strings to represent program elements! One of the big problems is that it hinders automated refactoring. Strings don’t have any semantic meaning, so refactoring tools don’t know which references to the string need to be updated.
While playing with expression trees, a while ago I came up with this:
public static class Argument { public static void NotNull(object value, Expression<Func<Func<object>>> arg) { if (value == null) { var body1 = (Expression<Func<object>>)arg.Body; var body2 = (MemberExpression)body1.Body; throw new ArgumentNullException(body2.Member.Name); } } }
You can use this for argument validation like this:
static void Main(string[] args) { Argument.NotNull(args, () => () => args); }
This looks a little strange, but has some interesting properties:
On the other hand it has some drawbacks too:
I’m not sure I would use this in production code, but it was an interesting exercise to come up with a way of validating parameters that doesn’t involve strings.
What do you think?
Just a quick update. After my previous post, I actually stopped using virtualization for most cases, since I found the performance hit to be too significant. While the automation I mentioned before still doesn’t support HyperV, I hand built a couple of VM’s with it and I have to say that I’m quite impressed.
On my host machine, it takes about 8 minutes to do a clean build of the code I usually work on. With my previous virtual machine, it took about 32 minutes! I think this happened because while my host machine has 4 processors, the VM can only access one of those. I wasn’t willing to pay that price. With HyperV today it takes about 9.5 minutes, and I am willing to pay that price for the added flexibility and the time I can save installing builds.
Lately I've been trying to support work happening in a variety of our development branches, and that's been challenging to the two machine setup I mentioned here. Visual Studio (unfortunately) takes a long time to install and uninstall. Also during our development cycle uninstall is sometimes broken. That means that people often re-image (at least) their test machines whenever they need to install a new build of Visual Studio.
In order to do work in three different branches somewhat simultaneously, I need to have test machines that have those three different branches installed. It wasn't reasonable to re-image and re-install my single test machine every time I needed to switch branches. It's also not very cost effective for me to have 3 test machines where two of them spend the vast majority of their time idle, not to mention that this doesn't scale well if one day I need a 4th branch.
So last week, I jumped on the virtualization bandwagon. I flattened my best machine, installed windows server 2008 64-bit*, and started using virtual machines for my test machines. So far it's been working out very well for me, but I do have to give credit to some other people. A few people in our test group have written some automation around Virtual Server to make life easy for people wanting to use virtual machines. They have some software that manages creating VMs for you. It basically does the following steps:
For step 7, this group has provided stock scripts for installing various versions of Visual Studio, and one to install the latest build from a selected branch. It's really convenient, since steps 2 to 6 only take about 25 minutes (and step 1 is usually skipped because the image is in the usually in your local cache).
They also have an option to have the VM be destroyed and re-created every night, which means that you can come in to work in the morning and have a fresh test machine with the latest build on it.
So, I started out need to have 3 test machines in 3 different branches, but the system is so easy to use that I've already built up a larger set than that. My current list of virtual machines is:
My only wish is that they update it to take advantage of Hyper-V soon to improve performance of the hosted machines.
* I previously had Vista 32-bit on the machine because at the time I set it up Server 2008 wasn't released yet, and there was a bug in the interim builds of visual studio that made install fail on 64-bit machines.
I'm still here, I've just been very busy. We're in the thick of planning out what we'd like to do for the next version of Visual Studio. If you have any suggestions, please drop them in the comments section. I'm most interested in the sorts of features that would be Visual Studio features (as opposed to language features), and the ones that apply while you are actually editing C# code.
Also, I've changed roles a bit. I'm now the dev lead for the C# IDE team, instead of an individual contributor on the team, so I'm learning what it's like to be a people manager.
I have a few ideas queued up for future blog posts, but I'm definitely interested in your questions too. What parts of features in the C# editor are you curious about? What about work at MS and our processes, is that a topic of any interest?
My colleague Kirill mentioned a post he had about named parameters, but I thought I would pull it up out of the comments, because I think it's pretty interesting. It's similar to a system I've been using to simplify writing data classes that I'll get up here one of these days.
There were some points about my previous post that I thought it might be worth bringing up in a new entry. It seems that in general, most people agree that the set of bool parameters are a bad idea. I found this a little surprising, given how often I've seen it. Maybe it's just the code-bases I've inherited :)
Someone pointed out that an alternative to using object initializers from C# 3.0 is to use Fluent Interfaces. Indeed, this is exactly what I would have done before object initializers were around. Jay and I have been talking about the idea of Fluent Interfaces and how they relate to "builder" classes for data classes for more than a year now.
Here's an example of how a Fluent Interface might be used instead of a collection initializer in the previous blog entry's context
struct DisplayUserOptions { bool email; bool phoneNumber; public DisplayUserOptions SetEmail(bool yes) { email = yes; return this; } public DisplayUserOptions SetPhoneNumber(bool yes) { phoneNumber = yes; return this; } }
And it can be called inline in a way similar to the object initializer syntax:
DisplayUser(user, Console.Out, new DisplayUserOptions().SetEmail(true).SetPhoneNumber(false));
Numerous people pointed that both the enum and the struct option produce more extensible API's than passing bools. In general I consider myself more of an application writer than a library writer, and so I tend not to worry as much about extensibility. This doesn't mean that I feel like I don't need to think about whether an API is usable or not, but rather that I don't tend to worry about changes that require me to recompile dependent assemblies. However, this does bring up a separate point. When I do code reviews I like them to contain meaningful changes, and so it's nice that with either the the enum or struct option the only places that change are the options type itself, and the callers who care about the new option. Conversely, with the bool option, all callers need to be updated.
I said in my previous post that one of the advantages of using structs is that it gives you a place to group behavior that belongs with the options. Someone pointed out to me that with C# 3.0, you can use extension methods to add functionality to your enums. This is a good point. I'll have to think more in the future about whether I feel comfortable using an enum together with some extension methods, as opposed to a struct or class.
Thanks for the great feedback! Keep it coming.
EDIT: Added an example of a Fluent Interface for this scenario.
EDIT 2: Fix the implementation of the Set methods in the example. Thanks Jay!
EDIT 3: Change section header to be "extension methods for enums", instead of "Extension methods for interfaces". Oops
I often find myself writing or consuming API's which require the caller to specify some sort of options. I've seen numerous ways to specify those options, but I've yet to find one that I really like. Let's work with an example throughout the rest of this post. Imagine we're working on an application which has some notion of a User. We're now about to create a function to output a User to a Stream. We want to provide two options:
I can imagine several ways of designing this API. First, we could use a couple of bool parameters, like this:
void DisplayUser(User user, TextWriter stream, bool displayEmail, bool displayPhoneNumber)
Next, we could use a flags enum to represent the options, like this:
[Flags] enum DisplayUserOptions { Email = 0x1, PhoneNumber = 0x2, Both = (Email | PhoneNumber), } void DisplayUser(User user, TextWriter stream, DisplayUserOptions options)
Finally we could create a struct which contains two bools instead of the enum, like so:
struct DisplayUserOptions { public bool Email; public bool PhoneNumber; } void DisplayUser(User user, TextWriter stream, DisplayUserOptions options)
struct DisplayUserOptions { public bool Email; public bool PhoneNumber; }
void DisplayUser(User user, TextWriter stream, DisplayUserOptions options)
I don't like passing the two bools, because I find that reading the callsite of a method designed like this to be difficult, because you don't know what the parameters mean anymore.
DisplayUser(user, Console.Out, true, false);
Does this display the email address or the phone number? I can't remember anymore. I know that parameter help can tell you the names of the parameters, but that doesn't help when I'm doing a code review in windiff, since windiff doesn't have parameter help tool-tips yet.
On my current team, we have a convention that when we call a method like this, we put the parameter name in a comment, so that we can see it, like:
DisplayUser(user, Console.Out, true /*displayEmail*/, false /*displayPhoneNumber*/);
However, in general, I don't like to rely on conventions that force people to put comments in a certain style to make the code readable. If possible, I'd rather design the API in such a way that it has to be readable.
The second option is using enum flags to represent the options.
DisplayUser(user, Console.Out, DisplayUserOptions.Email);
My problem with this approach is that it is hard to get right, both for the caller of the API, and the implementer. This example is sort of trivial, but I can remember a time when I was dealing with a bit-field that contained 31 unique bit values that could be set and unset independently. Getting all of the ~ and | and &'s just right was very hard, and once it was done, it was hard to figure out what it was trying to do. A final reason that I don't like enums is that in practice I often find that there are more behaviors that I want to be able to add to the options which isn't possible with enums. For example, it might be a requirement that two of the options are mutually exclusive. It's difficult to ensure that this is enforced with an enum.
Finally, we have the option of using a struct, which is addresses both of the two concerns above. You can write a call like:
DisplayUserOptions options = new DisplayUserOptions(); options.Email = true; options.PhoneNumber = false; DisplayUser(user, Console.Out, options);
Well, that's certainly clear. It also makes it easier to understand control flow that sets or clears the options, and to understand conditions based on them. It also gives a place to add that behavior: I can add methods to the struct, make the fields into properties which have setters that do validation, etc.
The problem with this approach is that in simple cases like the above, it is much more verbose than either of the other two alternatives. However, I recently realized that in C# 3.0, we can take advantage of Object Initializers to make the simple case simple again:
DisplayUser(user, Console.Out, new DisplayUserOptions { Email = true, PhoneNumber = false });
It's almost like having named parameters in C# :)
What do you think? Which alternative do you prefer? Do you have another one that I haven't thought of?
Another problem related to using VS in order to develop VS happens in the area of unit testing. The problem here is that my team would like to write tests for the C# language service in C#, unfortunately, this means that our dogfood IDE has the release version of the language service already loaded. Due to the way DLL loading works in windows, you can only have a DLL loaded with a given name, which means that the Unit Test framework is unable to load the debug copy of the language service that we just built. On our team, we work around that by building two versions of all of our assemblies, which are identical except for their names. That way we're able to load a unit-test specific version.
Unfortunately, this has other drawbacks. For example, it makes it impossible to combine code coverage results from unit tests, with the results from integration tests, since they are for different binaries.
There are a couple of issues that come up for developers who work on Visual Studio that just aren't issues for most people. The first is that working on components of VS while using VS is hard. It's hard for a couple of reasons.
Before we started working on VS2008 we had a single solution for both of these problems: Side by Side installs of Visual Studio. Back in the day, most developers who worked on Visual Studio didn't actually install Visual Studio through the setup process.
Instead we had a batch script which would copy the binaries from the daily build machine and then run regsvr32 on a huge list of different dlls in order to getting a "working" version of VS. This had some advantages, but also some very serious drawbacks.
Because of the second drawback above, the division made a decision at the end of VS2005, that we wouldn't continue to use batch setup for VS2008. Instead, we invested in updating our setup technology to make it easier for developers to build setup, and to understand how the setup gets built, so that it became easier to keep setup working. We also invested in some tools to make using an installed build easier. For example, we wrote a tools called "pupdate" that is able to update installed binaries from the location that they are built to automatically.
However, we never did get to the point where it was possible to install two VS2008 builds side by side to enable the experience above. How did dev's adapt to this? There were 3 different ways:
None of these are quite ideal, it'd be really nice to go back to the old days of being able to install 2 builds of VS side by side. We're thinking about investing in that for VS10, but as far as I know, it's still up in the air.
A friend sent me a link to Tomas Restrepo's collection of VS Color Schemes today. I think it's a fantastic idea. I like to use a dark background, but there sure a lot of settings that need to be changed in order for it to be usable. Especially if you use C++/C#/XML and XAML, which all have their own colors that need to be set...
I'll definitely be giving some a try when I get back in the office on Wednesday. So far I think Ragnarok Blue looks like it'll be my favorite.
Today being Thanksgiving here in the US (it's a month late, I know, but what can you do, eh?), my family spent the day making a big turkey dinner. My wife is usually the big cook in our house, but today she's got a bit of cold, so I ended up doing a bunch of the cooking, so that she could go and rest.
We had just gotten dinner ready, got the kids settled down, and I was helping them get their plates ready. I reached for the dish of stuffing, and it slipped out of my hand and shattered all over the table. No stuffing for anyone, but lots of crying from the disappointed kids. I think I'm going to have to make another batch of stuffing in the next few days...
In the end, the rest of the dinner was acceptable, and we all enjoyed it, but I'm really starting to feel like I'm in "Christmas Vacation"
On Sunday or Monday, I'll probably string some christmas lights, but I don't think they'll look anything like Eric's. Hopefully I'll manage to get them to actually turn on though.
A bunch of people (Eric, Wes, Joe, etc) have been writing about the wonders of immutable data structures lately. A friend who no longer works at MS suggested that I should dig up some code that he wrote with some feedback from me to try and help you enforce immutability in your code.
We started with the idea that we could use attributes to mark types as immutable. Then we could write some code that enforces that the Immutable attribute is actually satisfied. The rules are:
In practice, this fails very quickly, as soon as you bring in type that you didn't write, so there are some special exceptions:
Here's the code for those who are interested. I'll start with an example of problematic code we'd like to find. There is a problem with both of the types below. 'MutableStruct' is a problem because any time you have a mutable value type you have a problem in my opinion. It's far too easy to mutate the wrong copy of a struct, and spend ages trying to figure out where the bug is. 'MutableClass' is a problem because it lies. The author placed the ImmutableAttribute on it, signifying that they intend the type to be immutable, but it's actually not. The sort of time this is an issue is when you have a large amount of code which depends on the fact that MutableClass is immutable, and then you try to make a change that makes it mutable. Without a system like this, you may not realize that the original author intended MutableClass to be immutable.
struct MutableStruct { public int field; } [Immutable] class MutableClass { public int mutableField; }
Next, let's write some unit tests, to ensure that we don't ship this code. There are two unit tests here. The first one ensures that all structs are marked with the immutable attribute. The second verifies that all types marked with the ImmutableAttribute actually are immutable. It does that by calling into some code directly in the attribute, which is below. You might ask why this wasn't implemented as a Code Analysis, (aka FxCop) rule. The simple answer is that Jay and I were familiar with reflection and the unit testing framework, but not familiar with writing FxCop rules. If anyone wants to translate this into FxCop rules, I'd be interested in what that looks like.
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class Immutable_Tests { private IEnumerable<Assembly> AssembliesToTest { get { return new[] { System.Reflection.Assembly.GetExecutingAssembly() }; } } [TestMethod] // It's particularly important that 'struct' types are immutable. // for a short discussion, see http://blogs.msdn.com/jaybaz_ms/archive/2004/06/10/153023.aspx public void EnsureStructsAreImmutableTest() { var mutableStructs = from type in AssembliesToTest.GetTypes() where IsMutableStruct(type) select type; if (mutableStructs.Any()) { Assert.Fail("'{0}' is a value type, but was not marked with the [Immutable] attribute", mutableStructs.First().FullName); } } [TestMethod] // ensure that any type marked [Immutable] has fields that are all immutable public void EnsureImmutableTypeFieldsAreMarkedImmutableTest() { try { ImmutableAttribute.VerifyTypesAreImmutable(AssembliesToTest); } catch (ImmutableAttribute.ImmutableFailureException ex) { Console.Write(FormatExceptionForAssert(ex)); Assert.Fail("'{0}' failed the immutability test. See output for details.", ex.Type.Name); } } internal static bool IsMutableStruct(Type type) { if (!type.IsValueType) { return false; } if (type.IsEnum) { return false; } if (type.IsSpecialName) { return false; } if (type.Name.StartsWith("__")) { return false; } if (ReflectionHelper.TypeHasAttribute<ImmutableAttribute>(type)) { return false; } return true; } static string FormatExceptionForAssert(Exception ex) { StringBuilder sb = new StringBuilder(); string indent = ""; for (; ex != null; ex = ex.InnerException) { sb.Append(indent); sb.AppendLine(ex.Message); indent = indent + " "; } return sb.ToString(); } } Without further ado, here's the ImmutableAttribute itself. Note that some of the code is written to use the "ReflectionHelper", which is a utility class that provides some (IMO) useful wrappers around System.Reflection. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] [Serializable] public sealed class ImmutableAttribute : Attribute { // in some cases, a type is immutable but can't be proven as such. // in these cases, the developer can mark the type with [Immutable(true)] // and the code below will take it on faith that the type is immutable, // instead of testing explicitly. // // A common example is a type that contains a List<T>, but doesn't // modify it after construction. // // TODO: replace this with a per-field attribute, to allow the // immutability test to run over the rest of the type. public bool OnFaith; /// <summary> /// Ensures that all types in 'assemblies' that are marked /// [Immutable] follow the rules for immutability. /// </summary> /// <exception cref="ImmutableFailureException">Thrown if a mutability issue appears.</exception> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")] public static void VerifyTypesAreImmutable(IEnumerable<Assembly> assemblies, params Type[] whiteList) { var typesMarkedImmutable = from type in assemblies.GetTypes() where IsMarkedImmutable(type) select type; foreach (var type in typesMarkedImmutable) { VerifyTypeIsImmutable(type, whiteList); } } static bool IsMarkedImmutable(Type type) { return ReflectionHelper.TypeHasAttribute<ImmutableAttribute>(type); } class WritableFieldException : ImmutableFailureException { protected WritableFieldException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal WritableFieldException(FieldInfo fieldInfo) : base(fieldInfo.DeclaringType, FormatMessage(fieldInfo)) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)")] static string FormatMessage(FieldInfo fieldInfo) { return string.Format("'{0}' is mutable because field '{1}' is not marked 'readonly'.", fieldInfo.DeclaringType, fieldInfo.Name); } } class MutableFieldException : ImmutableFailureException { protected MutableFieldException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal MutableFieldException(FieldInfo fieldInfo, Exception inner) : base(fieldInfo.DeclaringType, FormatMessage(fieldInfo), inner) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)")] static string FormatMessage(FieldInfo fieldInfo) { return string.Format("'{0}' is mutable because '{1}' of type '{2}' is mutable.", fieldInfo.DeclaringType, fieldInfo.Name, fieldInfo.FieldType); } } class MutableBaseException : ImmutableFailureException { protected MutableBaseException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal MutableBaseException(Type type, Exception inner) : base(type, FormatMessage(type), inner) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)")] static string FormatMessage(Type type) { return string.Format("'{0}' is mutable because its base type ('[{1}]') is mutable.", type, type.BaseType); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] public class ImmutableFailureException : Exception { public readonly Type Type; protected ImmutableFailureException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal ImmutableFailureException(Type type, string message, Exception inner) : base(message, inner) { this.Type = type; } internal ImmutableFailureException(Type type, string message) : base(message) { this.Type = type; } } /// <summary> /// Ensures that 'type' follows the rules for immutability /// </summary> /// <exception cref="ImmutableFailureException">Thrown if a mutability issue appears.</exception> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")] public static void VerifyTypeIsImmutable(Type type, IEnumerable<Type> whiteList) { if (whiteList.Contains(type)) { return; } if (IsWhiteListed(type)) { return; } try { VerifyTypeIsImmutable(type.BaseType, whiteList); } catch (ImmutableFailureException ex) { throw new MutableBaseException(type, ex); } foreach (FieldInfo fieldInfo in ReflectionHelper.GetAllDeclaredInstanceFields(type)) { if ((fieldInfo.Attributes & FieldAttributes.InitOnly) == 0) { throw new WritableFieldException(fieldInfo); } // if it's marked with [Immutable], that's good enough, as we // can be sure that these tests will all be applied to this type if (!IsMarkedImmutable(fieldInfo.FieldType)) { try { VerifyTypeIsImmutable(fieldInfo.FieldType, whiteList); } catch (ImmutableFailureException ex) { throw new MutableFieldException(fieldInfo, ex); } } } } static bool IsWhiteListed(Type type) { if (type == typeof(Object)) { return true; } if (type == typeof(String)) { return true; } if (type == typeof(Guid)) { return true; } if (type.IsEnum) { return true; } // bool, int, etc. if (type.IsPrimitive) { return true; } // override all checks on this type if [ImmutableAttribute(OnFaith=true)] is set ImmutableAttribute immutableAttribute = ReflectionHelper.GetCustomAttribute<ImmutableAttribute>(type); if (immutableAttribute != null && immutableAttribute.OnFaith) { return true; } return false; } } [Serializable] internal static class ReflectionHelper { /// <summary> /// Find all types in 'assembly' that derive from 'baseType' /// </summary> /// <owner>jayBaz</owner> internal static IEnumerable<Type> FindAllTypesThatDeriveFrom<TBase>(Assembly assembly) { return from type in assembly.GetTypes() where type.IsSubclassOf(typeof(TBase)) select type; } /// <summary> /// Check if the given type has the given attribute on it. Don't look at base classes. /// </summary> /// <owner>jayBaz</owner> internal static bool TypeHasAttribute<TAttribute>(Type type) where TAttribute : Attribute { return Attribute.IsDefined(type, typeof(TAttribute)); } // I find that the default GetFields behavior is not suitable to my needs internal static IEnumerable<FieldInfo> GetAllDeclaredInstanceFields(Type type) { return type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); } /// <summary> /// A typesafe wrapper for Attribute.GetCustomAttribute /// </summary> /// <remarks>TODO: add overloads for Assembly, Module, and ParameterInfo</remarks> internal static TAttribute GetCustomAttribute<TAttribute>(MemberInfo element) where TAttribute : Attribute { return (TAttribute)Attribute.GetCustomAttribute(element, typeof(TAttribute)); } /// <summary> /// All types across multiple assemblies /// </summary> public static IEnumerable<Type> GetTypes(this IEnumerable<Assembly> assemblies) { return from assembly in assemblies from type in assembly.GetTypes() select type; } }
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class Immutable_Tests { private IEnumerable<Assembly> AssembliesToTest { get { return new[] { System.Reflection.Assembly.GetExecutingAssembly() }; } } [TestMethod] // It's particularly important that 'struct' types are immutable. // for a short discussion, see http://blogs.msdn.com/jaybaz_ms/archive/2004/06/10/153023.aspx public void EnsureStructsAreImmutableTest() { var mutableStructs = from type in AssembliesToTest.GetTypes() where IsMutableStruct(type) select type; if (mutableStructs.Any()) { Assert.Fail("'{0}' is a value type, but was not marked with the [Immutable] attribute", mutableStructs.First().FullName); } }
[TestMethod] // ensure that any type marked [Immutable] has fields that are all immutable public void EnsureImmutableTypeFieldsAreMarkedImmutableTest() { try { ImmutableAttribute.VerifyTypesAreImmutable(AssembliesToTest); } catch (ImmutableAttribute.ImmutableFailureException ex) { Console.Write(FormatExceptionForAssert(ex)); Assert.Fail("'{0}' failed the immutability test. See output for details.", ex.Type.Name); } }
internal static bool IsMutableStruct(Type type) { if (!type.IsValueType) { return false; } if (type.IsEnum) { return false; } if (type.IsSpecialName) { return false; } if (type.Name.StartsWith("__")) { return false; } if (ReflectionHelper.TypeHasAttribute<ImmutableAttribute>(type)) { return false; } return true; } static string FormatExceptionForAssert(Exception ex) { StringBuilder sb = new StringBuilder(); string indent = ""; for (; ex != null; ex = ex.InnerException) { sb.Append(indent); sb.AppendLine(ex.Message); indent = indent + " "; } return sb.ToString(); } }
Without further ado, here's the ImmutableAttribute itself. Note that some of the code is written to use the "ReflectionHelper", which is a utility class that provides some (IMO) useful wrappers around System.Reflection.
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] [Serializable] public sealed class ImmutableAttribute : Attribute { // in some cases, a type is immutable but can't be proven as such. // in these cases, the developer can mark the type with [Immutable(true)] // and the code below will take it on faith that the type is immutable, // instead of testing explicitly. // // A common example is a type that contains a List<T>, but doesn't // modify it after construction. // // TODO: replace this with a per-field attribute, to allow the // immutability test to run over the rest of the type. public bool OnFaith; /// <summary> /// Ensures that all types in 'assemblies' that are marked /// [Immutable] follow the rules for immutability. /// </summary> /// <exception cref="ImmutableFailureException">Thrown if a mutability issue appears.</exception> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")] public static void VerifyTypesAreImmutable(IEnumerable<Assembly> assemblies, params Type[] whiteList) { var typesMarkedImmutable = from type in assemblies.GetTypes() where IsMarkedImmutable(type) select type; foreach (var type in typesMarkedImmutable) { VerifyTypeIsImmutable(type, whiteList); } } static bool IsMarkedImmutable(Type type) { return ReflectionHelper.TypeHasAttribute<ImmutableAttribute>(type); } class WritableFieldException : ImmutableFailureException { protected WritableFieldException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal WritableFieldException(FieldInfo fieldInfo) : base(fieldInfo.DeclaringType, FormatMessage(fieldInfo)) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)")] static string FormatMessage(FieldInfo fieldInfo) { return string.Format("'{0}' is mutable because field '{1}' is not marked 'readonly'.", fieldInfo.DeclaringType, fieldInfo.Name); } } class MutableFieldException : ImmutableFailureException { protected MutableFieldException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal MutableFieldException(FieldInfo fieldInfo, Exception inner) : base(fieldInfo.DeclaringType, FormatMessage(fieldInfo), inner) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)")] static string FormatMessage(FieldInfo fieldInfo) { return string.Format("'{0}' is mutable because '{1}' of type '{2}' is mutable.", fieldInfo.DeclaringType, fieldInfo.Name, fieldInfo.FieldType); } } class MutableBaseException : ImmutableFailureException { protected MutableBaseException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal MutableBaseException(Type type, Exception inner) : base(type, FormatMessage(type), inner) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)")] static string FormatMessage(Type type) { return string.Format("'{0}' is mutable because its base type ('[{1}]') is mutable.", type, type.BaseType); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] public class ImmutableFailureException : Exception { public readonly Type Type; protected ImmutableFailureException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } internal ImmutableFailureException(Type type, string message, Exception inner) : base(message, inner) { this.Type = type; } internal ImmutableFailureException(Type type, string message) : base(message) { this.Type = type; } } /// <summary> /// Ensures that 'type' follows the rules for immutability /// </summary> /// <exception cref="ImmutableFailureException">Thrown if a mutability issue appears.</exception> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")] public static void VerifyTypeIsImmutable(Type type, IEnumerable<Type> whiteList) { if (whiteList.Contains(type)) { return; } if (IsWhiteListed(type)) { return; } try { VerifyTypeIsImmutable(type.BaseType, whiteList); } catch (ImmutableFailureException ex) { throw new MutableBaseException(type, ex); } foreach (FieldInfo fieldInfo in ReflectionHelper.GetAllDeclaredInstanceFields(type)) { if ((fieldInfo.Attributes & FieldAttributes.InitOnly) == 0) { throw new WritableFieldException(fieldInfo); } // if it's marked with [Immutable], that's good enough, as we // can be sure that these tests will all be applied to this type if (!IsMarkedImmutable(fieldInfo.FieldType)) { try { VerifyTypeIsImmutable(fieldInfo.FieldType, whiteList); } catch (ImmutableFailureException ex) { throw new MutableFieldException(fieldInfo, ex); } } } } static bool IsWhiteListed(Type type) { if (type == typeof(Object)) { return true; } if (type == typeof(String)) { return true; } if (type == typeof(Guid)) { return true; } if (type.IsEnum) { return true; } // bool, int, etc. if (type.IsPrimitive) { return true; } // override all checks on this type if [ImmutableAttribute(OnFaith=true)] is set ImmutableAttribute immutableAttribute = ReflectionHelper.GetCustomAttribute<ImmutableAttribute>(type); if (immutableAttribute != null && immutableAttribute.OnFaith) { return true; } return false; } } [Serializable] internal static class ReflectionHelper { /// <summary> /// Find all types in 'assembly' that derive from 'baseType' /// </summary> /// <owner>jayBaz</owner> internal static IEnumerable<Type> FindAllTypesThatDeriveFrom<TBase>(Assembly assembly) { return from type in assembly.GetTypes() where type.IsSubclassOf(typeof(TBase)) select type; } /// <summary> /// Check if the given type has the given attribute on it. Don't look at base classes. /// </summary> /// <owner>jayBaz</owner> internal static bool TypeHasAttribute<TAttribute>(Type type) where TAttribute : Attribute { return Attribute.IsDefined(type, typeof(TAttribute)); } // I find that the default GetFields behavior is not suitable to my needs internal static IEnumerable<FieldInfo> GetAllDeclaredInstanceFields(Type type) { return type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); } /// <summary> /// A typesafe wrapper for Attribute.GetCustomAttribute /// </summary> /// <remarks>TODO: add overloads for Assembly, Module, and ParameterInfo</remarks> internal static TAttribute GetCustomAttribute<TAttribute>(MemberInfo element) where TAttribute : Attribute { return (TAttribute)Attribute.GetCustomAttribute(element, typeof(TAttribute)); } /// <summary> /// All types across multiple assemblies /// </summary> public static IEnumerable<Type> GetTypes(this IEnumerable<Assembly> assemblies) { return from assembly in assemblies from type in assembly.GetTypes() select type; } }
What do you think? Is this sort of verification of immutability useful?
Okay, anyone whose been holding their breath for the last 1.5 years for part two of this series, I'm going to have to disappoint you. I have no idea anymore what it was that I was planning on writing about in Part 2 of that series.
However, I wanted to dust off the old blog, because I have an idea that I want to blog about.
So I said on Saturday that I would write a future post about the performance issues I've been seeing. Instead I'm going to write two separate posts. First because, I don't entirely understand both issues, and second, because I think this one might get a little bit long. The first issue I noticed you won't find in either Beta1 or Beta2, because it was recently introduced. The issue is that after you open a file, or make an edit outside of a method/property body, the next time that you try to bring up a completion list, it takes a really long time. (about 5 seconds on my dev machine, which is pretty powerful).
The reason for this is a little complicated, so I'm going to go into some more background about the C# language service, much like Cyrus does in this post. One of the things we do when bringing up a completion list is determine what methods and types to display in it. We generally do that by walking up the scopes from the cursor position, and adding everything we see. However, sometimes we do more than that. For example, if you are in a bases and interfaces list, and not on the first type in the list, we'll only show namespaces and interfaces or types that contain nested interfaces. In order to do that, we ask each type: are you an interface, or do you have a nested interface, or a nested type that contains a nested interface. We need to show types that have nested types that have interfaces, because you may want to type that nested interface, and if we don't show you the containing type, the completion list is just getting in your way. Pretty simple really.
The problem is that our logic wasn't quite right. The issue is that it's possible to access a nested type in one of your super types, and we didn't put those nested types in the list. Type for an example I think:
class Base {
public interface IBase {
}
class Derived : Base {
class C {}
class D : C, // HERE {
At the comment, it's valid to type "Derived.IBase", but we wouldn't show "Derived" in the completion list. In order to fix this issue, we added code to an internal function "GetSuperType()", and checked to see if it has any nested types we would want to show.
Okay, so why does that create a perf problem you might ask. The problem has to do with our implementation of GetSuperType. Getting the super type of an object is a fairly expensive operation for us, because we essentially need to reparse the file to find it's bases and interfaces list, and then determine the new super type. Once we find the super type, we then cache it for future lookups, which helps out performance most of the time. The problem is that every time we reparse an entire file, we clear the cache of every super type. That's because adding or removing a type may cause what the super type is to change (a new type closer in scope with the same name might be added, meaning we should re-bind to that type for example).
In my example above, when you open a file, we re-parse the file, in order to do things like populate the error-list, calculate the collapsible regions, and populate the navigation bar (those two drop-downs at the top of the file). This causes our cache to be invalidated, and the next time you need a completion list, we have to go and re-bind the super type for every type in your containment hierarchy, and any nested types inside them, which causes quite a hit.
Okay, so how are we going to address the situation? Well, we have three ideas.
We're currently investigating option number 1. What do you all think.
We finally got it out there.
Take a look at msdn.
Or, to see it from the big guy, Soma's blog.
Please go get this, and let us know what you think.
It's been ages since I've posted anything here, but I'm going to try and get back into the swing of things.
So what's new with me.
Well, I don't have much time to write right now, as my wife is in the process of picking my in-laws up from the airport and I'm supposed to be tidying up.
Not much to report for today, as I didn't have much of a chace to attend the show content. I was kept pretty busy at the Microsoft booth talking to people and showing them some of the new stuff we've been building for Whidbey.
Tomorrow should be an exciting day, as Herb Sutter is presenting on C++/CLI and it's relevance to modern programming, and the agile community. At the same time Alex Goyen is giving a talk about the Microsoft Solutions framework (v4), and using Visual Studio as a platform for agile development.
So today I arrived in Calgary for the Agile/XP Universe conference. I'm really excited to be here and get a chance to talk to some of the people who are here.
Had quite a day of travelling, as I got a shuttle to the airport this morning. It picked me up at 5:50 am for my 9:48 flight. I cleared security before 6:45, and had to wait 3 hours before my flight left. Then I landed in Edmonton for an hour, and went looking through the airport for some Tim Hortons. Of course all I could find was a Seattle's Best Coffee (ah, the irony). Anyway, I got to the hotel at about 4:00pm, called an Aunt and Uncle who live here in Calgary (and who I haven't seen for about 10 years) and made plans to go out for dinner at 6:30. Then I decided to go down and check out the conference area. So down I go to the 3rd floor of the Hyatt. I stepped out of the elevator and there were a bunch of people standing around, so I went to register. Apparently, I was the first person from Microsoft to arrive, so they asked me to set up our booth. Now I've never been to a conference except as a regular guest, so this was a new one to me, but finally I got it all set up, and went to the intro session, where I volunteered to talk to some people about Refactoring support in Visual Studio tomorrow afternoon. At that point it was 6:20, so I rushed down to the lobby and met my Aunt and Uncle for a nice dinner a little pub just down the street from the hotel. Then back to the room to talk to my wife for a few minutes, and finally a quick swim. Now I'm heading off to bed.
Just wanted to let you all know that I'm here, and I'll try to fill you in about what's going here at the conference.
When the conference is over, I'll be heading off to Toronto (my home town) for 2 weeks vacation, including my wife's and my 5th anniversary next Saturday.
A while back I asked for some feedback about how we should format anonymous methods. Then I was asking whether the default should be to put the opening brace on the same line as the delegate keyword or not. From your feedback, it looks like we'll keep the default the same as it is now (on the next line). So that brings me to another question. Some people have requested the ability to have the anonymous method braces indented. I'll give some examples:
Currently, anonymous methods look something like this:
List<Thing> somethings = things.FindAll(delegate(Thing thing)
{ return thing.ShouldBeIncluded; }
);
Or this:
{ return thing.ShouldBeIncluded; });
{
return thing.ShouldBeIncluded;
});
Depending on where you put newlines.
With the proposed option, you would be able to make those look like:
Or even this:
So my question for you today is: Do you want the new option? If you do, what should the default be, indented, or not indented?
Please post and let me know.
We’re considering making two changes to the way the formatting engine works, based on feedback we’ve received from Beta 1. I’d like to post them here, and see what you all think of them, to make sure we’re making the right decision.
Change 1: Comments that start at column 1.
In Beta 1, if you have a comment that starts at column 1 (the very left edge of the screen), we won’t change it’s indentation. The main reason for this behaviour is that if you select a block of text and use Edit->Advanced->Comment selection, we place all the ‘//’ that we generate at the left edge, and we wouldn’t want a subsequent formatting to push them way in.
We’re finding that this behaviour has bad effects in two main ways.
Our proposed solution to this is to make the formatting engine always indent comments, and fix our “Comment Selection” behaviour to generate comments at the indent of the line with the least indentation in a block. For example comment selection would now generate something like this:
public void Method()
List<int> list = new List<int>();
li.Add(3);
//foreach (int i in list)
//{
// Console.WriteLine(i);
//}
Instead of:
// foreach (int i in list)
// {
// }
Change 2: Anonymous methods default to having the open brace on the same line.
This change is mainly for readability. Basically we want to change the default value of “Tools->Options->Text Editor->C#->Formatting->New Lines->New line options for braces->Place open brace on new line for anonymous methods” from true to false. The reason for this is that we think that if you have code like this:
List<Thing> things;
void SomeMethod()
List<Thing> somethings = things.FindAll(delegate(Thing thing) {
It’s easier to tell that it’s part of an anonymous method than:
Or:
All of which look sort of like you just have a random block.
So what do you think? Are we on the right track, or is there a better way to solve these problems?