Entries
RSS 2.0

Comments
RSS 2.0

Generic Variance in C# 4.0

Although I’m not cool enough to actually go to PDC, I’ve been watching some of the things that have been announced. One of the things I’m most excited about is co- and contra-variance in generics, which is something that the CLR has lacked since generics were first introduced in 2.0. (Note: some of the examples on here are pulled from the excellent description of new features release by Microsoft.)

In versions of C# prior to 4.0, generics were invariant. For example, consider this simple type definition:


public class Foo<T>
{
  //...
}

Since the generic type parameter T was not constrained, the compiler understands that T should be treated as type object. That means that since a string is an object, a Foo<string> is functionally equivalent to a Foo<object>. However, because of generic invariance, ian instance of Foo<string> cannot be assigned to a variable of type Foo<object>.

C# 4.0 introduces the ability to declare covariant and contravariant generics. For example:


public class Foo<out T>
{
  //...
}

This class is covariant in T, meaning that if you create a Foo<string>, you can use it effectively as a Foo<object>, since a string is a subclass of object. The example given is the new IEnumerable<T> interface that comes with the BCL in C# 4.0:


public interface IEnumerable<out T> : IEnumerable
{
  IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IEnumerator
{
  bool MoveNext();
  T Current { get; }
}

Since these interfaces are covariant in T, an IEnumerable<string> can be used as an IEnumerable<object>. The same is true for List<T>, so you’ll be able to do this, which was previously impossible:


IList<string> strings = new List<string>();
IList<object> objects = strings;

Note, however, that you can only declare that your type is covariant for generic type parameters that appear in output positions — basically, return values.

Like the out keyword, you can also use the in keyword:


public interface IComparer<in T>
{
  int Compare(T left, T right);
}

This interface is contravariant in T, meaning that if you have an IComparer<object>, you can use it as though it was a IComparer<string>. Contravariance carries a similar restriction to covariance, in that contravariant type parameters can only be used in input positions (arguments) on the type.

I didn’t quite understand variance until I considered the changes to the Func delegate:


public delegate TResult Func<in TArg, out TResult>(TArg arg);

Func is contravariant in TArg, and covariant in TResult. For example, consider this (less-than-useful and slightly-contrived) method:


public string Convert(object obj)
{
  return obj.ToString();
}

With the new variance rules, I can do this:


Func<object, string> func1 = Convert;
Func<string, object> func2 = Convert;

Since the delegate is contravariant in TArg, I can pass in a string, since a string is an object. Since the delegate is covariant in TResult, I can use it in a situation where an object is expected as a result from the function.

This might seem a little overwhelming if you’re not a language geek like myself, but it basically means that things that seemed like they should work in previous versions (like the List<T> example above), now will just work. Eric Lippert also has a great series of posts about the topic.

If you’d like to tinker with C# 4.0 (and Visual Studio 2010), Microsoft has published a VPC image. Kudos to the C# and CLR teams for getting this stuff to work!

Be the Underdog

Last night, I watched my Cleveland Browns wipe the floor with the New York Giants. The Browns were 1-3, and if they didn’t win this game, their season was effectively over. The Giants, reigning Superbowl champions, came into the game undefeated at 5-0, and it wasn’t believed the Browns had a snowball’s chance in hell to win. In response, the Browns beat them by three touchdowns, without punting or turning the ball over once. They had their problems, but it was a night of complete dominance for Cleveland.

Even if you’re not interested in sports, there’s a lesson to be learned. The Browns came into the game as major underdogs, with nothing to lose. They used it to their advantage, and their gameplan became aggressive and creative. They successfully ran trick plays like reverses and direct snaps, and the only successful execution of the UFO defense that I’ve seen… well, ever. The Giants didn’t see it coming, and by the time it hit them, there was nothing they could do but sit on the sidelines with puzzled looks on their faces.

37signals suggests that when developing a product, you should choose an enemy to compare with. By recognizing what’s wrong with the competition, you can find a niche that you can fill.

I’d take it one step further and see yourself as David, and choose a Goliath to attack. Embrace your role as underdog, unleash your creativity, and hold nothing back. Even if you’re already the industry leader, don’t get cocky — choose the company with the second-largest market share, and attack.

Most importantly, don’t allow yourself to be constrained by the way your competition or the industry-at-large thinks. That’s where real innovation comes from.

Custom Selection Heuristics in Ninject

I haven’t blogged about Ninject for awhile, but that doesn’t mean that the project is entirely dormant. :) Version 1.5 will be coming out relatively soon, but I wanted to give a preview of a new feature that I think is pretty damn cool. It’s currently available in the Subversion trunk if you’d like to flex the bytes yourself.

Every dependency injection framework needs to have some sort of convention or marker to indicate dependencies — spots where the framework should resolve a value to inject. When I originally wrote Ninject, I made the decision to use [Inject] attributes as this marker. In fact, I still advocate the use of attributes to indicate dependencies, because I like the declarative nature. I’ve found that even if you don’t completely understand Ninject, or dependency injection in general, using attributes at least indicates that a value comes from somewhere. It can be a great conversation starter — something you put in your code that when another developer sees it, it makes them ask questions.

Anyhow, I’ve found that the reliance on attributes was a major turnoff to other people. Ninject has always been (and will remain) opinionated software, but I’ll always add flexibility as long as I’m not compromising the core goals of ease of use and efficiency. As a result, I’ve been building in more control over these selection heuristics — the conditions that Ninject uses to test members of a type to determine which should be considered injectable members. The first step of this was to create the auto wiring extension, which alters the heuristic to determine whether a binding exists for a given member’s type — and if so, it will inject it.

Ninject 1.5 builds on this idea by moving to a collection of heuristics rather than just one. Now, when Ninject examines a member on a type to determine if it should be injected, it evaluates all of the related heuristics, and if any of them match, the member will be injected. There are now also two levels of heuristics:

  1. Global heuristics: As the name suggests, these apply to all bindings. You set them directly on the newly-created IMemberSelector component.
  2. Binding-level heuristics: These are set directly on a specific binding, so you can set up your own conventions for a specific type.

When examining a member, the member is first tested with the global heuristics, and then with the binding-level heuristics for the binding that is being used to activate the instance.

Heuristics are specific to the type of member they inspect. The types of members that Ninject considers are constructors, properties, methods, and fields, and each has a matching type of heuristic. When a property is considered for injection, Ninject will only test it using all applicable IHeuristic<PropertyInfo>s, but won’t consider IHeuristic<ConstructorInfo>s, for example.

The easiest way to control heuristics en masse is by defining your own IMemberSelector. By default, Ninject uses the StandardMemberSelector, which registers heuristics that look for the [Inject] attribute on the members — or, if you’ve overridden the attribute via KernelOptions, it will examine that. However, if you prefer a more conventions-based approach, you can use the ConventionMemberSelector:


public class OurKillerAppMemberSelector : ConventionMemberSelector
{
  public override DeclareHeuristics()
  {
    InjectProperties(When.Property.Name.StartsWith("Foo"));
    InjectConstructors(When.Constructor.HasAttribute<MarkAttribute>());
    InjectMethods(m => m.Name.Length < 4);
  }
}

This will cause all properties beginning with Foo, and all constructors with the (fictional) [Mark] attribute to be injected. Methods with names less than 4 characters will also be injected, as as you can see, you can specify conditions using lambdas (predicates) instead of using the conditional infrastructure.

If you want finer-grained control over your selection heuristics, you can also declare them on individual bindings as I said before:


Bind<IService>().To<ServiceImpl>()
  .InjectConstructors(c => c.Arguments.Length == 1)
  .InjectMethods(When.Method.Name == "Prepare")
  .InjectProperties(p => p.Name.EndsWith("Service"))

In this situation, when Ninject activates an instance of ServiceImpl, it will call the constructor on the type declared with one argument. It will then call the method called Prepare (resolving values for each of the method’s arguments), and resolve and inject values into each property whose name ends in Service.

Hopefully now you can see why I’m so excited about the new selection heuristics in Ninject. Out of the box, Ninject will continue to rely on the [Inject] attribute for simplicity, but you now have an incredible amount of flexibility in setting up your application, your way.

Working from home

Today ended my sixth week working for Telligent. Like most of the other members of the product development team, I’m working remotely from home. It’s been a very interesting adjustment, and I figured I’d share my experiences.

First, the good stuff:

  1. You can control your working environment. For the first three weeks, I sat in an old cheapo Staples desk chair at my old uncomfortable desk, until I was able to barter with my wife to let me re-organize the office. I’m now sitting in a Herman Miller Mirra chair at a much more ergonomic desk from Ikea. I have a comfortable (cushioned) chair in my office where I can sit, relax, and think. I can play music through speakers without needing to worry about coworkers, and I can close my office door to talk on the phone, video-conference, or if it just gets too noisy. I’m a natural control freak when it comes to environment while I work, and after working in a “bullpen” environment (one big room without cubes), this is a huge improvement. Incidentally, if you’re wondering, yes, the Mirra is expensive, and I was skeptical too, but it’s worth every penny. It’s so comfortable that I actually look forward to sitting in it.
  2. You can’t beat the commute. I’ve been fortunate not to have to drive more than 35 minutes or so to work for any job I’ve held, but just knowing that I don’t have to get in the car and drive across town is a nice feeling. Not to mention the savings on gas is great, and I suspect that once the Ohio winter strikes, I’ll like the fact that I don’t have to leave the house even more! I’ve also found that my days seem longer, and once I get through the transition period I’m hoping to devote my newfound spare time to side projects like Ninject and Ideavine, as well as a couple of other things I have up my sleeve. Who knows, I might even blog more than once a month! :)
  3. You get to spend more time with your family. My wife is a graduate student, and she’s able to work from home most of the time. It’s nice to be able to spend additional time with her, even if it’s just having lunch or taking a break to talk with her for a few minutes every couple of hours. For others, being able to spending time with children is great. My wife and I don’t have any children, but we do have a couple of dogs, and it’s nice to be able to have them around while I work. They tend to cry sometimes, but they’re starting to learn that when I’m sitting at my desk, they need to relax and wait for me to pay attention to them.
  4. Working without pants is finally a reality. Just kidding. :)

Of course, working at home isn’t without some challenges:

  1. It’s easy to get distracted. Being at home brings with it a certain mindset. It’s important to keep in mind that when you’re working, you’re working, and while you can take short breaks, you need to stay on task or it’s easy to meander off. It’s easier to get off-task when you’re in the “at home” mindset. I think this is largely because of the transition — although I’m used to working on side projects and moonlighting consulting from home, I’m not used to doing it for my “day job”. I’ve noticed as I’ve gotten more accustomed to it, I’ve become more effective at remaining on task.
  2. It’s even easier to work too much. Since your office is your home, your home is also your office, and so you never really “punch out”. I’m also a natural workaholic — programming is more “fun” to me than “work” a lot of the time — so it’s easy for me to come back into the office after dinner and continue to work, even though I really should be done for the day. As part of my transition into telecommuting, I’m trying to draw stronger lines between work-time and off-time.
  3. Communication with the rest of the team is more difficult. Fortunately, Telligent has a pretty firm grasp on how to manage remote workers. Video conferencing makes a huge difference in terms of ease of communication — for whatever reason, seeing the other person’s face as they speak makes a dramatic difference both in the efficiency and quality of the conversation. We use a pretty lightweight Agile practice, and instead of daily stand-ups, we have daily video conferences via Tokbox. (The tool isn’t the best, but so far it’s better than any others that I’ve tried for multi-party video chat.) In terms of communication, the continuum is (with increasing efficiency): Twitter, email, IM, IRC, voice, video, then real-life.
  4. You become even more reliant on technology. Our cable internet was knocked out by the remnants of Hurricane Ike a few weeks ago, and was out for 3 days. We were fortunate enough not to lose power for more than a few minutes, but I was forced to go to my wife’s office at the University in order to work. When you telecommute, losing your internet connection is kind of like having your car break down — except you can’t get a rental or bum a ride to work. We’ve since invested in business-class cable, and while it doesn’t help with widespread outages, I at least get a guarantee of same-day or next-day service. At least I have my wife’s office to fall back on if need be — although I suppose I could always be a wifi leech at Panera. :)

All in all, I have to say that so far the benefits vastly outweigh the detriments. Maybe I’m biased, though, since I’m really interested in the products that Telligent makes, and am jazzed by the stuff I get to work on. (They actually pay me to do this stuff! Suckers. :D)