Finding a Better Overload
Tuesday, February 3rd, 2009One of the latest issues I’ve been working on with C5 is to close the loop on a few compilation warnings that are not #pragma warning warnings. The last of these involves CLS compliance. The C5 library currently advertizes itself as CLS-compliant, but it actually is not. The one example that I am currently wrestling with is the IDictionary<K,V>.Find() overloads. There are two overloads for the Find() method. Their signatures are:
bool Find(K key, out V value); bool Find(ref K key, out V value);
These two methods are unambiguously distinguished in C# because the compiler requires the use of ref or out as the method signature dictates. However, for other languages, such as Boo, this “by reference” argument passing is inferred. The Common Language System dictates that each overload of the same method must differ by more than just the pass-by type of its arguments (CS3006).
[CLSCompliant(false)] attribute to the method in question and move along, probably creating a new, compliant method and marking the old one obsolete. The confounding issue is that a core C5 principle is programming to interface. Thus, every method (save one or two) on the public classes is implementing a method defined in an interface, including this one. Interfaces do not have the option of having certain methods be non-CLS compliant. It’s all or nothing (CS3010).
This leaves me in a bit of a catch-22. I cannot ensure cross-language accessibility without changing the interface method signature, but that introduces a breaking change. I have a feeling that both method implementations are widely used among C5 users too.
The first overload is apparent as the default use case, but the second overload is useful when your key may be prototyped. For example, in our dictionary we have a Person as the key and a Account as the value. The person has a bunch of information on it, but the equality comparer is only concerned with the personId:
public class Person : IEquatable<Person> {
private int personId;
public string Name {get; set;}
...
public bool Equals(Person other) {
return this.personId.Equals(other.personId);
}
}
Then if we knew the personId we could construct a prototype of the person and use the Find() method to get not only their account, but the actual person.
var person = new Person(1);
Account account;
if (Accounts.Find(ref person, out account)) {
Console.WriteLine("Found {0}'s account", person.Name);
} else {
Console.WriteLine("Account not found");
}
So, with that example in mind, how do I proceed? The current idea I have is to mark the second overload [Obsolete], create a new overload or method, and deal with the non-CLS compliance for a minor release or two before removing the non-compliant overload. Perhaps I should remove the non-compliant overload outright, like a band-aid.
As well, what should the new method signature be? After thinking for a while, here are my two top choices:
bool Find(K key, out KeyValuePair<K,V> pair); bool FindWithKey(ref K key, out V value);
Which of those two would be better from an API user’s point of view? My instinct says the second one is most clear, but the first follows the existing naming. What’s your take?







