tags:

views:

531

answers:

6

The title basically spells it out. What interfaces have you written that makes you proud and you use a lot. I guess the guys that wrote IEnumerable<T> and not least IQueryable<T> had a good feeling after creating those.

+1  A: 

I guess the guys that wrote IEnumerable … had a good feeling after creating [it].

I'm not so sure. IEnumerable was quite a failure, as interfaces go. This is evident from the fact that IEnumerable<T>, its generic counterpart, actually offers a completely different interface. (Well, “different” isn't a good word; basically, it adds disposability and de-emphasizes the completely useless Reset method; however, this method is of course still present.)

Additionally, similar languages that have similar constructs (Java comes to mind) have a much better, more extensible construct to fulfill the same needs (and more). For example, Java's iterators can be extended to be bidirectional or to allow modifying access (whereas IEnumerable is always read-only).

EDIT: Since there's so much controversy in the comments, let me clarify. I'm not saying that IEnumerable (or IEnumerator) are bad interfaces. They're adequate. However, they could have been made better. If nothing else, then at least the Reset method seems like clutter.

Terjet said that he “use[s] it all the time” – which is precisely my point! IEnumerable is a pivotal interface of the whole .NET framework. It's ubiquitous. There'd be no .NET without it. Is it therefore too much to ask for a perfect interface?

“Adequate” is just another word for failure.

Konrad Rudolph
That could be, I could maybe refrase my question to be about IEnumerable<T> and IQueryable<T>. I still think still IEnumerable<T> is very useful and successful because it is so extensively used. (I use it all the time)
TT
In what way is IEnumerable<T> a "completely different interface"? It's exactly the same, just generic. And then the only difference between IEnumerator<T> and IEnumerator is that IEnumerator<T> extends IDisposable, which is useful for iterator blocks.
Jon Skeet
(Beyond being generic, of course - and you can't blame the IEnumerator designers for generics not being present in the framework...)
Jon Skeet
I also disagree on the extensible nature - many iterators simply aren't suited for being writable or bidirectional, and as soon as you can't really rely on an interface being fully implemented, those semi-optional members become a lot less useful. (Even Reset in IEnumerator was a mistake IMO.)
Jon Skeet
@Jon: About the difference: see the clarification in my posting. Basically, the `Reset` method was a big mistake. About the extensibility: I can't really speak for Java but C++ shows quite conclusively that iterators can be implemented *a lot* better.
Konrad Rudolph
It doesn't "de-emphasize" Reset - it just inherits it from IEnumerator because it doesn't need to change the signature. Otherwise you should apply the same logic to MoveNext() which is pretty crucial! Disposal is the only real difference. As for C++ iterators... somewhat different, IMO.
Jon Skeet
(Continued) The differences between generics and templates allow things to work pretty differently between C++ and Java/C# ideas of interfaces. I'm happy with IEnumerable<T> though - other than Reset, I think it's a fine interface.
Jon Skeet
Why is Reset() such a bad idea?
SDX2000
In many cases it just doesn't make sense - you can't reset a stream of data which has come from the network, for instance (assuming you haven't buffered it all).
Jon Skeet
I agree with Konrad on this. Of course it's useful to have a forward-only iterator like IEnumerable, but it's crippled in that there is no support at all for more complex bidirectional or random-access ones. I'm not impressed by IEnumerable<T>.(Although fitting LINQ on top of it is very neat)
jalf
Many things are possible (such as iterator blocks) *because* IEnumerable is so limited. That's not to say that other more complex APIs aren't useful - IList<T> etc - but it would be hard to implement things like LINQ and iterator blocks over those more complex interfaces.
Jon Skeet
Jon, I agree with you that LINQ wouldn't work (or even need) another iterator. That's not what I said. C++ also has algorithms that only work on output iterators (which is basically what IEnumerable models).
Konrad Rudolph
So there should be different interfaces to model more capable iterators - that's fine. Just don't force me to throw exceptions because I can't actually implement everything - having a simpler interface (like IEnumerable) is much better. In short, I really disagree with calling IEnumerable a failure.
Jon Skeet
"I'm not impressed by IEnumerable<T>" - neither am I, but then I don't think it's intended to be impressive in itself. It's minimalistic, which gives it generality, which makes it widely applicable, and all the applications viewed together are impressive.
Daniel Earwicker
@Jon, yes. Of course I didn't suggest stuffing the `IEnumerable` interface with more members. I just don't see how a sensible hierarchy can be built on top of `IEnumerable`.
Konrad Rudolph
@Earwicker: I, on the other hand, *would* be impressed by a parsimonious interface. I'm a big fan of simplicity. IEnumerable+IEnumerator are not very simple however.
Konrad Rudolph
@Konrad: There are other interfaces already built on IEnumerable: IOrderedEnumerable, for example, and IList etc. Other interfaces could also be built on top of IEnumerator, just as Java has ListEnumerator with extra functionality. I get the feeling we're going to have to agree to disagree though.
Jon Skeet
One final point: when did "adequate" and "failure" become synonymous? Two definitions: adequate: "sufficient for the purpose"; failure: "an event that does not accomplish its intended purpose". Sorry, but they're just *not* the same at all.
Jon Skeet
What would you remove from IEnumerable+IEnumerator to achieve simplicity? Most people would remove Reset, but surely everything else is essential to the concept of iteration. It minimally captures the notion of a forward iterator.
Daniel Earwicker
@Earwicker: Exactly. Reset shouldn't be in there, and IEnumerator should have extended IDisposable in the first place, but that's all I'd change. In particular, I prefer the .NET model of MoveNext()/Current to the Java model of hasNext()/next(). In my experience it's easier to work with.
Jon Skeet
+3  A: 

I'm pleased with the design of the interface at the heart of Push LINQ. It's a very simple interface, but with it you can do all kinds of interesting things. Here's the definition (from memory, but it'll be pretty close at least):

public interface IDataProducer<T>
{
    event Action<T> DataProduced;
    event Action EndOfData;
}

Basically it allows observers to "listen" on a stream of data instead of "pulling" from it in the way that IEnumerable works.

Three points of interest:

  • The name sucks. I want to rename it, and I've had some good feedback, but the name isn't there yet.
  • The event handlers don't follow the standard .NET conventions of having a sender/event args. In this case it really doesn't make much sense to do so.
  • The multicast nature of events makes this perfect for calculating multiple aggregates etc. All that comes for free.
Jon Skeet
Conveniently, it works happily with variance in C# 4.0 (I checked on the CTP a while ago ;-p). Good job we didn't err and put the "do a push" method into the interface.
Marc Gravell
Have you considered the security implications of not using EventHandler delegates? e.g. If your code has full trust then partially trusted code could register Environment.FailFast as the handler for IDataProducer<string>.DataProduced and have it run under a full-trust context.
Greg Beech
I hadn't considered that, but if they're running under a full-trust context couldn't they just call it themselves?
Jon Skeet
@Greg: After thinking about it overnight, I think I may know what you mean. I think I'd change it if it ever became an issue, but for now it's fine.
Jon Skeet
+5  A: 

Strictly an interface? Or just an API? I'm kinda pleased with how the generic operator stuff worked out (available here) - I regularly see people asking about using operators with generics, so I'm glad it is a handy answer for a lot of people. It might be slightly easier in C# 4.0, but I very much doubt it will be as fast - the DLR-tree/dynamic stuff has an overhead.

I'm also quite happy that it was helpful in the Push LINQ that Jon has already mentioned ;-p

Marc Gravell
Like Marc, I'm not sure whether it really counts for this question, but I can certainly confirm that his generic operator stuff rocks :)
Jon Skeet
Marc, that rocks. Oh, you already knew …?
Konrad Rudolph
A: 

I am working on a validation system I plan on releasing to the community soon. It is essentially an implementation of the Specification pattern.

The core interface is designed to be functional in nature:

public interface ICheckable<T>
{
    CheckResult Apply(T target);
}

CheckResult is a struct representing a tri-state value: Passed, Failed, and Ignored. All of the conversion and operator overloads are in place to treat it as a Boolean value.

This allows validators to express "I have no opinion" instead of returning a misleading true value (think RangeValidator indicating an empty field is valid so it plays nice with RequiredFieldValidator).

Composition comes naturally and is done with static classes a la Linq. Each dot into the check is an implicit And of the next operation:

public static ICheckable<T> Add<T>(this ICheckable<T> check, ICheckable<T> otherCheck)
{
    return new Check<T>(t => check.Apply(t) && otherCheck.Apply(t));
}

public static ICheckable<T> Either<T>(this ICheckable<T> check, ICheckable<T> firstCheck, ICheckable<T> secondCheck)
{
    return check.Add(t => firstCheck.Apply(t) || secondCheck.Apply(t));
}

public static ICheckable<T> Not<T>(this ICheckable<T> check, ICheckable<T> negatedCheck)
{
    return check.Add(t => !negatedCheck.Apply(t));
}

Extension methods work nicely:

public static ICheckable<int> Percentage(this ICheckable<int> check)
{
    return check.Add(n => n >= 0 && n <= 100);
}

public static ICheckable<T> GreaterThanOrEqualTo<T>(this ICheckable<T> check, T value) where T : IComparable<T>
{
    return check.Add(t => t.CompareTo(value) >= 0);
}

public static ICheckable<T> LessThanOrEqualTo<T>(this ICheckable<T> check, T value) where T : IComparable<T>
{
    return check.Add(t => t.CompareTo(value) <= 0);
}

public static ICheckable<T> Range<T>(this ICheckable<T> check, T minimum, T maximum) where T : IComparable<T>
{
    return check.GreaterThanOrEqualTo(minimum).LessThanOrEqualTo(maximum);
}

// RangeExcludeMinimum
// RangeExcludeMaximum
// RangeExclusive

Each operation includes overloads which take a check-building lambda:

public static ICheckable<T> Add<T>(this ICheckable<T> check, Func<ICheckable<T>, ICheckable<T>> makeCheck)
{
    return check.Add(makeCheck(new IgnoredCheck<T>()));
}

So you can do syntax like this:

ICheckable<int> check;

check.Add(i => i.Percentage().GreaterThan(50).Even());
Bryan Watts
Any reason why Apply isn't called Check? That's what I'd expect based on the result and the interface name.
Jon Skeet
Haha good catch :-) The interface was originally ICheck<T> but I renamed it to represent its deferred nature. I will probably implement this suggestion.
Bryan Watts
A: 

This is an ActionScript 3 interface which was the core of our for the Flash Player's new behaviour in as3.

public interface IDisposable {
  public function dispose():void;
}

As you'd expect, the dispose method should close any resources and drop any references to anything it possibly can.

C++ programmers may scoff at this 'innovative' interface (which is totally fair enough), but as3 introduced a whole lot of issues surrounding memory management in Flash. These issues are all "old hat" for many compiled languages, but actionscript programmers are just now experiencing these challenges for the first time.

Yes, it's still a garbage collected language. But for better or worse, there's a lot less 'hand-holding' than in ActionScript 2, as evidenced by the need for this interface.

aaaidan
+1  A: 

For doing MVP-pattern work I have a few basic framework interfaces:

public interface IValidatable {
  bool IsValid { get; set; }
  void ShowValidationFailureMessage(string message);
}

public interface ISubmitable {
  event EventHandler Submit;
  void ShowSubmitFailureMessage(string message);
  void ShowSubmitSuccessMessage(string message);
}

public interface ICancelable {
  event EventHandler Cancel;
}

With these 3 interfaces I can write presenter which has these generic operations (which cover basically all form operations). Eg:

public interface ILogin : IValidatable, ISubmitable, ICancelable {
  string Username { get; set; }
  string Password { get; set; }
}

You can then create a presenter and stub it out.

Slace