1
votes

Fiddler's HTTPHeaders class has a member, Storage, which is a List<HTTPHeaderItem>.

The HTTPHeaders class exposes an enumerator in the .NET 1.1 style thusly: public System.Collections.IEnumerator GetEnumerator(). This method simply returns Storage.GetEnumerator().

Questions:

  1. Is there a performance penalty to not implementing IEnumerable<HTTPHeaderItem>? Or is the compilation of foreach(HTTPHeaderItem in oHTTPHeaders) smart enough to recognize that the object it got back also implements the type-specific version of IEnumerable and avoid a bunch of casting?

  2. Is it even possible to implement IEnumerable<HTTPHeaderItem> without hiding (via explicit interface implementation) the older GetEnumerator() method? Hiding the old method breaks binary compatibility with legacy extensions (e.g. Method not found: 'System.Collections.IEnumerator Fiddler.HTTPHeaders.GetEnumerator()'.)

Outcome

*As noted by Servy's answer below, there's no simple way to avoid breaking binary compatibility when adding a public GetEnumerable() that returns a type-specialized Enumerator.

However, in my specific scenario, I got a bit lucky. HTTPHeaders is a base class and virtually all callers are using one of two derived classes (HTTPRequestHeaders and HTTPResponseHeaders). I've added public generic GetEnumerator() methods to both of the each of the derived classes. These new type-specific enumerators are called by code that is compiled against the new version, while older plugins that haven't been recompiled still retrieve the non-generic Enumerator from the base class.*

1
You could implement IEnumerable<HTTPHeaderItem> explicitly. The non-generic version would be the exposed one, and shouldn't break compatibility. I don't know which one a foreach loop would use, though. - David Yaw
As elaborated in Servy's answer, foreach() ends up using the public one. - EricLaw

1 Answers

5
votes
  1. You need to provide some GetEnumerator method that returns the actual HTTPHeaderItem type for casting to be avoided. This means either implementing IEnumerable<HTTPHeaderItem> or having the one implicit GetEnumerator method return the actual type. In the general case, this means you can rely on Duck typing, and not implement IEnumerable<T>, but in your case, since such a method already exists with the wrong signature, that's not possible.

  2. Sure. Just hide the IEnumerable<HTTPHeaderItem> implementation instead of the IEnumerable implementation. This is exactly what arrays do (to maintain binary compatibility).

Here's a simple example:

public class Foo : IEnumerable, IEnumerable<HTTPHeaderItem>
{

    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator<HTTPHeaderItem> IEnumerable<HTTPHeaderItem>.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}