Best behavior when you can’t perform an action the user specified.


Another interesting issue came up today with a C# IDE feature. It involved the following piece of code:

interface IFoo<T> {
    void Bar<U>(U u) where U : T
}

class MyFoo : IFoo<int> {
}

We have a feature called “implement interface” which Anson blogged about here. We offer you the option to implement the interface implicitly or explicitly. The reason that this is interesting is that in the above code you cannot implement the interface implicitly. Why not? Because you would end up with the following code:

class MyFoo : IFoo<int> {
     public void Bar<U>(U u) where U : int
}

The problem is that the “U : int” constraint is not valid and is not something that can be expressed in the runtime. We also can’t produce:

class MyFoo : IFoo<int> {
     public void Bar<U>(U u)
}

Because you’re not necessarily implementing all the methods of IFoo and the runtime won’t allow that either. The only way you’re allowed to actually implement that specific method of the interface is to do:

class MyFoo : IFoo<int> {
     void IFoo<int>.Bar<U>(U u)
}

In that case you don’t need to put on the constraints (they’re known since you’re explicitly stating what interface method you’re implementing) and everyone is happy. Unfortunately, there’s an issue with this. The user just stated “i want to implement the interface implicitly”. What do you do? We came up with a few options each with what we could do:

  1. Implement the method incorrectly, i.e. with the “U : int” constraint. Won’t compile and won’t be very clear how to fix the problem
  2. Implement all the methods we can do implicitly implicitly, and any method we can’t we do explicitly. It works but we didn’t do what you asked
  3. Pop up a dialog stating “we can’t implement this interface, would you like to implement explicty: ‘yes’, ‘cancel’? It works but it adds an extra step to the process of implementing the interface
  4. Don’t offer the smart tag option to implement the interface implicitly. It prevents the issue but it would be completely unclear why we didn’t show it. (I would think that) most people would think some weird intellisense bug was occurring.

How do you think that this sort of situation should be handled? Each has the plusses and minuses. However, with many intellisense features we generally try to go with the model that you should be able to perform the action and go about proceding with coding extrmely quickly. Dialog boxes interrupt that flow, questions interrupt that. The flip side of that is that you want to be able to perform intellisense actions and understand exactly what will happen. The more ‘error tolerance’ we add, the more unclear it becomes to the user how it’s going to behave. Where is the line drawn in these sort of areas?


Comments (26)

  1. Most likely, I would intend to do option 2) if I knew that not every method can be implemented implicitly. I would want to see both options in the intellisense, but perhaps the implicit option would have some sort of disclaimer attached (or its wording changed) so that it’s clear there’s a problem with full implicit implementation.

  2. Haacked: Any ideas on the wording (it’s hard!! And I’m pretty sucky at it!)?

    saying something like "try to implement implicitly" would indicate we might fail (totally). "Implement Implicitly (not all might be implemented implictly)" is kind of wordy :-)

    See Anson’s picture on this to see how it’s presented to the user.

    BTW: what were your reasons for picking it over the other options?

  3. David Levine says:

    I don’t think that most people make the distinction between implicit and explicit when they ask the IDE to implement the interface, they just want all the necessary methods stubbed out.

    The first time it occurred I prefer the IDE to popup a dialog that asks the user how to proceed – give them the options you listed – and have a checkbox "Dont’ show this dialog again unless you hold down the <shift> key.". Save the results and always do it that way until they reconfigure the setting.

    In addition to options 1 & 2 I’d offer another option, to do it all explicitly.

  4. Radu Grigore says:

    Not directly relevant but.. I have recently been so annoyed by a program that continously tried to guess what I mean instead of saying "something is wrong" that I decided never to use it again.

  5. Cyrus,

    I think that either option 3 or 4 would be the best approach. With option 3, it only comes up in that specific situation, which is fine, and I would be appreciative that I was informed of the issue. I think that this alludes to your previous post on refactoring, where refactoring should always do the right thing. Asking the user if they want to do something else because the current course of action is not possible is the right thing.

    If you go with option #4, then I would recommend a tool tip popping up over the menu item when hovering over it which indicates why it is disable. Actually, this might be good for most menu items that are disabled. It would definitely go a long way to help people understand their environment.

  6. Nicole Calinoiu says:

    Different users will want different outcomes, so why not give us the choice, along with all the information we need to make an intelligent decision? Your option #3 could be modified to offer this sort of flexibility. i.e.:

    1. Message box should list the methods that cannot be implemented implicitly.

    2. Message box should offer the following choices (not necessarily in this order):

    a. Implement all explicitly.

    b. Implement implicitly where possible, explicitly where not.

    c. Implement all implicitly even though this won’t compile.

    d. Cancel the implementation.

    3. Add an option to not show the dialog in the future unless, for example, the shift key is depressed.

    Additionally, regardless of which the non-cancel options is selected, write a "results" message to the output window indicating which path was taken. This would be particularly handy for folks who choose not to have the dialog displayed.

  7. Juan Felipe Machado says:

    The cases yo have exposed are SO rare…. I would’nt care if intellisense works fine 99% of the times I use it and wrong only 1%!!! I think the case is so rare that every option is valid..

    If you want to ask me what to do in a dialog and interrupt the flow of intelisense, do it. As long as you do that ONLY on the extreme cases you expose.

    If you want to produce code that won’t compile, it’s OK!! because it’s ONLY 1% of the times, i can live with that.

    …etc

  8. Jeff says:

    Option 2, definitely. I want the product to do what I mean (generate stubs for the interface), even if I didn’t ask for it quite right.

    Putting up a dialog to ask me is VERY WRONG, but a non-modal toolwindow kind of thing explaining what has been done and listing some things I might want to do instead is EXCELLENT. The guiding principle here is "don’t interrupt me when I’m working", and it has the virtue of being discoverable.

  9. Sorin Dolha says:

    I would go with Option 3 or Option 4, the latter, improved, as Nicholas Paldino said (show a tooltip instead of the SmartTag explaining the problem). Option 3 would be OK too.

    Sorin Dolha [MCAD, MCSD .NET]

  10. DarthPedro says:

    I think option 3 is the best. Let the user know what is wrong before trying to create anything. Then, leave it to the user to pick which change is appropriate.

    Trying to guess and change the code to explicit is going to be wrong for some people. And, undoing that refactoring won’t be much fun for them.

  11. David: Take a look at anson’s page http://weblogs.asp.net/ansonh

    We already offer you the option to do it implicitly or explicitly. The issue is when you choose the implicit option and we _can’t_, under any circumstances, do it implicitly.

    I’m thinking an alternative is to offer two choices in the smart tag:

    a) Implement interface

    b) Implement interface explicitly

    The first does what it can, make most methods implicit, the second does every method explicitly.

  12. Radu: What program was that?

  13. Nicholas: I agree. Unfortunately, our menus lack the ability to be disabled and to show a tool tip.

    We could, however, completely disable the item and say:

    a) Implement interface implicitly (unavailable, interface cannot be implemented implictly)

    b) Implement interface explicitly

    THen the interaction model is the same. There’s no extra step, and we’ll produce the correct code for you.

  14. Thanks for all the feedback!

    There’s another option that we play around with these kind of tools. We just go ahead and make all the changes, but we dump information to the output window. So you’d implement the interface and in the output window (Which you can think of as a log) we’d say:

    ‘Implemented method Bar explicitly as Foo<int>.Bar because it had constraints that couldn’t be expressed implictly.’

    Thoughts on that model?

  15. Gil Tayar says:

    Implement it like #1, but commented out with an explanation of why it is commented out. That way – no annoying dialogs, a clear explanation of the problem – and the ability for the developer to uncomment and deal with the problem

  16. I like your last idea. The reason is that I would hate for the implicit option to be grayed out just because one method of many cannot be implicitly implemented.

    Then in those rare cases, the IDE won’t save me much time as it could have.

    I prefer having the IDE output the fact that several methods couldn’t have been implemented implicitely.

  17. David says:

    Option 2. Implementing that function explicitly is the only way to implement the interface. Someone has to do it. It should be the Intellisense. Otherwise, you’re just going to force the user to do some extra typing.

  18. Scot Boyd says:

    Use option 3, except say "Cannot implement implicitly due to method X" so the user sees what the problem is.

  19. Ugh: Why can’t your feedback be consistent so that it’s obvious what choice to make :-)

    I should just say (a la Ford) "you can have any behavior as long as it’s the one we choose" 😉

  20. Nicole Calinoiu says:

    Cyrus,

    WRT your last suggestion, would it be possible to remove the interface implementation with a single undo operation? If not, I would still much prefer to have at least the option of cancelling the implementation if it can’t be implemented as requested. After all, the troublesome interface might be one that I just authored myself, and I might prefer to go back and re-examine its design if I encounter this sort of problem on its first implementation.

    I’m also not quite sure why you and so many of the commenters are so very opposed to the idea of using a dialog to request the preferred outcome. If an option were added for suppression after initial display, both the "flow" folks and the control freaks would be able to work as they prefer. After all, is having to use potentially inconvenient dialog once per VStudio install really all that horrible?

  21. Nicole: Excellent points.

    WRT dialogs. I’m not adamantly opposed ot them. I do, however, worry about going down the path where every issue becomes something that we ask the user about (Even if we provide a checkbox saying remember this choice). I’m concerned that in that sort of a model every time an issue comes up it’s far too easy to resolve as ‘oh lets just ask the user’. In many cases we think it’s quite important to ask, but in others we feel taht we don’t need to ‘spam’ you and can provide a quite intelligent choice otherwise.

    Note: if after long discussion we decide that that isn’t the case, but an option would solve this then we will use the option. See the previous thread on passing type params around by reference.

    However, we don’t take user interaction decisions lightly. We know that they can and will affect how people work and we want to make the choice that tries to have the most positive impact and least negative impact. Talking to you guys is a way to tell how good/bad these chocies will be.

  22. Radu Grigore says:

    "Radu: What program was that?"

    Poseidon for UML CE.

  23. Nicole Calinoiu says:

    Cyrus: Guess I wasn’t really being explicit enough regarding why I made the option suggestion in the first place…

    For this particular situation, I just can’t see any one path being a good general approach. After all, the main goal of the "implement interface" functionality would supposedly be to save the user both time and effort. Even though the situation that causes the potential problem is expected to be rare, users probably shouldn’t ever run into a scenario where using the "implement interface" functionality costs them more time and effort than just manually typing out all the stubs.

    Unfortunately, for each of the possible paths, I can think of at least one example where implementing with that path would end up being a worse approach than just slogging through the typing. If individual developers choose to punish themselves in this way, that’s their problem. However, if you lock a particular behaviour into the tool, we’re might all be suffering sooner or later.

    It’s also probably worth considering that the typical needs of API developers and end-product developers are very different wrt this issue. There are many more of the latter, so you’ll definitely want to accomodate their preferences. However, if you don’t meet the needs of the API developers, the burden of suffering might very well be transferred to their clients, those very same end-product developers.

    By the bye… If you’re running into this sort of decision problem with many features, I would definitely agree that the dialog route is not very attractive. However, this doesn’t mean that another approach to offering the options wouldn’t be acceptable to most users. It’s probably way too late to integrate a new settings mechanism into Whidbey, but here’s what my own personal preference would lean toward:

    1. Each of this type of option bundled under some "decisions" tree in the VStudio options hierarchy.

    2. Settings from #1 would be overrideable, where appropriate, at the solution and project levels.

    3. Settings could be easily transferred between users, solutions, and projects.

    4. Named settings "bundles" could be defined and applied to users, solutions, and projects. VStudio should probably ship with a few pre-defined bundles to facilitate initial setup for developers who work mainly in one role.

    Folks who never discover the functionality or just plain don’t care would essentially be no worse off than if you were to choose one path for all of us. Obviously, some special care would be required to ensure that solution/project settings don’t cause any nasty surprises for these users, but I’ve already rambled on long enough, so I’ll just stop right here. <g>

  24. Nicole: Thanks for the response. I jsut found this now (outlook had categorized it as spam).

    I would like some clarification of what you mean by "Unfortunately, for each of the possible paths, I can think of at least one example where implementing with that path would end up being a worse approach than just slogging through the typing"

    Could you give examples of each?