Now where did I put that member?


Got this Ladybug report from a customer concerned that intellisense was broken because he wasn’t seeing all the valid members in a drop down list when he did this:

        public void DoSomethingOfRelevance()

        {

            PrintPreviewDialog ppDialog;

            ppDialog<dot>

        }

Specifically, items in the list that were missing were properties like “WindowState”, “PrintPreviewControl” and others.  Oddly enough I’d just debugged an incredibly similar matter with Daigo concerning System.Windows.Forms.CheckedListBox and the DataSource property.  He was incredibly frustrated and came to me to tell me that Intellisense was broken and that he should be seeing this member in the list.  At first I thought it must be something wrong with our metadata reader.  If we can’t understand something from metadata we don’t read it in and proceed to the next thing in the assembly (this is actually pretty common as many languages out there produce things we can’t understand (like properties with multiple arguments (whee! nested parenthesis are fun!))).  However, in this case it turns out that we were reading it in just fine.  So what could be the problem?  Anyone out there have an idea?

*chirp*

Well, I could wait for ideas, but I should just tell you.  As it turns out the member was marked in metadata as having the following property: 

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

From the docs you can see that the attribute is used to: “Specifies that a property or method is viewable in an editor” and that the value passed in states: “The property or method is never browsable from within an editor”.   And, if you look at the DataSource property you’ll see that it has these docs: “This member supports the .NET Framework infrastructure and is not intended to be used directly from your code.”  Note: there is also a state called “Advanced” and one can view items marked as advanced if they toggle

Tools | Options | Text Editor | All Languages | Statement Completion | Hide Advanced Members.

Now, the reason we read it in from metadata is that we need to make sure that we know about that member so that it will appropriately hide members.  Using Daigo’s example, if we were to forget about that member then we’d place ListControl.DataSource in the list (ListControl is a base type of CheckedListBox) which would be really bad as you’d think that that was the datasource you were accessing when in actuallity your compiled code would access the datasource in the derived type.

Personally, I find this behavior incredibly confusing, frustrating and scary.  As a developer one rightly gets quite confused when the docs tell you one thing and intellisense seems to tell you something else.  One also wonders why in this design one would make these properties public but then not expect others to use them.  It’s scary because I don’t think that intellisense should be filteringout legal things without making the user aware (see this previous post on that topic).  I was thinking about doing something different so that this would be a lot clearer.   My ideal solution would be to have the element in the completion list, but with a strikethrough through it.  The tooltip would also say something along the lines of “Warning.  The author of this component did not intend for it to be used externally” followed by the actual documentation on it.  Unfortunately, it does not look like one can have that much control over the font in the completion list in VS2005.  So, as an alternative, I could grey out the text and have the tooltip updated as well.

How do you feel about this?  I might be personally biased here, but this “feature” really rubs me the wrong was and I definitely think we should do better.  If we don’t, I think we’ll have a log of confused and irate developers on our hands.


Comments (23)

  1. Ben Monroe says:

    I think greying it out makes sense. Strikethrough, though, would initially make me think that the method is deprecated, which is not the intention.

  2. Ben: Fair enough. I was going for that "you really don’t want to use this" type message.

  3. Jeff Parker says:

    I do like Dr Pizza’s reply on your referenced post about this. If it sees you typing it and you can do it maybe not legally or maybe not a good idea then start popping it up. The other draw back to this is even right now if you type it. You get no intellisense help at all with any arguments and intellesesense is gone completely from any and all things having to do with that member.

    Like you said maybe you shouldn’t use this method. Hmmm, but what if thats explicitly what you are planning to do and you are aware of what it does and this is what you want to do. I mean if this method is so bad to use why not let the compiler catch it? You would never do that because of the rare chance that someone is using something in a way MS never dreamed of. So why cut it from Intellisense?

  4. Mark Boyle says:

    Would it make sense to adopt the same approach as the menus in Windows and Office, i.e. hide them by default and have an icon at the bottom which expands to show all of them?

  5. Jeff: "So why cut it from Intellisense? "

    Well, partly because that’s how EditorBrowsableState.Never is defined to work. I agree though that’s it’s extremely weird.

    I’m going to try out some of these completion models, however at first glance I’m definitely wary. Adding more emmbers in as you type has the potential to be very confusing to a user. If you start with a lot of members and slowly filter out as you type then at least a user can tell what’s happening and why. If you go the reverse direction I can imagine a lot of "where did that guy come from??"

    I also see people doing:

    foo<dot>a<delete>b<delete>c<delete>d

    trying to find out if there are any members they should know about.

  6. Radu Grigore says:

    I think that any way of showing those methods in the editor will break their documentation.

    I also think that using attributes to get something like a "custom visibility" thru interaction with editor features is a bad design. Maybe you can dig and find out why does that attribute exist? Maybe it’s obsolate? Maybe PrintPreviewDialog should be fixed?

    Or maybe my opinions are non-sense because I don’t yet understand the "world of .net metadata".

  7. Radu Grigore says:

    BTW, I don’t suggest "blame the others". From your entry it is apparent that you don’t know exactly why is that attribute used (I may be wrong, though). What I suggest is that finding this out is an important step in solving the problem.

  8. Radu: Why would it break their documentation?

    "I also think that using attributes to get something like a "custom visibility" thru interaction with editor features is a bad design"

    No arguments from me.

    —-

    However, the "why" is obvious. A developer out there decided that they wanted to have a public member that would only be used by certain people, not anyone generally consuming their code. It may be something that needs to be fixed, it may not. However, this attribute exists today and people are using it. The question is "what’s the right way for us to listen to this attribute?" We’ve chosen a certain implementation now, but it is by no way necessarily the right one.

  9. Radu Grigore says:

    "Why would it break their documentation?"

    Well, you said that the documentation says: "The property or method is never browsable from within an editor". If you make it browsable (even by IntelliSense) you break this. It seems simple. Am I missing something?

    "A developer out there decided that they wanted to have a public member that would only be used by certain people, not anyone generally consuming their code"

    By "why" I mean: who’s that develoder, why did (s)he decided somethink like this and who are those "certain people". My guess is that answering these questions can give you some hints on what is the correct course of action. Even if this a particular occurence of this problem, others might share some characteristics.

    Without knowing the answer to these questions, the grayed out solution sounds best.

  10. Jeff Parker says:

    Well, you do pose a good point, The Atribute is there to not show a method. But if I explicitly type in that method I would want the intellisense that goes with it. I guess thats my only problem with this. Once you type it in the editor then shows nothing.

    The only reason I bring it up is I have done this. I can’t remember why, this was a long time ago I can’t remember which project it was for. I was looking for a specific way to do something and this member allowed me to do it. I remember posting this question on windowsforms.net which intellesense wouldn’t work with a specific member nor show up in intellesense. I never got any replies I even questioned if it was a bug or not. Obviously someone else thought it was a bug as well otherwise you wouldn’t have been given the bug to look into.

    I looked through and read all the documentation on a method. So I looked it up, I understood what I was doing and still I wanted to do it. So I had to type everything in in and flip around to make sure I had all my arguments correct. I guess so maybe you leave it hidden. But if someone explicitly types it in then turn on the supporting intellisense for it. Does that model make more sense. Because if someone knows it is there. They have obviously done their homework on it and want to use it. I don’t know it seems more to me this attribute is there more to protect the Mort and Elvis styles from doing something bad, but the Einstein type knows it is there and sees something there that may just suit his needs. Like I said I spent a lot of time trying to figure out why this one member wouldn’t show up. I did everything from using Lutz reflector to decompile it to googling on it. So I really researched this method more than most typical ones where I read the MSDN on it and roll with it.

  11. Jeff Parker says:

    Sorry double post, I got a wierd BlogDoesNotExistException on first post change it tried again with same result. Yet they both posted. can you delete one of those. And this one.

  12. Radu: Ah, i see. I was confusing the documentation for the EditorBrowsableState class and teh documentation for the CheckedListBox.DataSource propety.

    Note: the issue comes as to whether or not our implementation is the best for the user. "Not showing the member" is an example of a possible implementation of this API. We’re considering alternative implementations that don’t suffer from the same issues that arise from the current one.

  13. Jeff: We will bind to the method just fine and will give you parameter help for it. All we currently do is hide it in the completion list.

  14. Jeff: We will bind to the method just fine and will give you parameter help for it. All we currently do is hide it in the completion list.

  15. Jeff Parker says:

    Hmm, not this one. I distinctly remember it not giving me any intellesense what so ever. Like I said I could type it in but got no help with it at all, noargs or anything. I wish I could remember exactly what it was for but I was confounded by it not giving me any intellesense at all. It was also all part of the .net Framework as well. Standard framework not mobile Since I don;t do Mobil code. Also I am pretty sure it was 1.1 of the framework, Cause I tested it both at home and at work just to see if there was something wonkey with my machine at work. And my PC at home never had VS 2002 on it. Could there be another reason you would get absolutely no intellesense for a member? Like I said it didn’t show in the list at all, but according to MSDN it existed and I did reflect it out. I do not remember any unusual Args on it but it would just not show up or give me intellesense. But I could type it in end the line with a semicolon and hit compile and it worked. Man I wish I remember what this was. I just assumed from your description in this blog entry this described everything I seen and was sharing my experiences of dealing with this.

  16. Jeff: "Could there be another reason you would get absolutely no intellesense for a member? "

    In a word: Yes :-)

    The reason being:

    a) We suck…

    b) Faulty logic in the code

    hehehe :-)

    Seriously, there are bugs and they do lead to missing members sometimes. We’ve worked very hard to eliminate them but it does happen. While we’re very confident about the VS2k5 codebase we don’t have absolute confidence. That’s why when Daigo came in saying "intellisense is missing a member" my heart sank because I thought there was something we missed.

    If you ever do find out what that member was let me know so I can test to make sure it’s not a problem now!

  17. Jeff Parker says:

    Hah, well that’s a reason 😉

    We all suck a little, and we all did buggy code at one point and time and maybe in the future. Coders are only human.

    I will see if I can find that member but might be a difficult hunt. God only knows how many lines of code I have written in VS2003. From Windows Services to Web Sites. And just as I get really comfortable in thinking I know most of the Framework, you have to go and come out with framework 2.0 😉 Oh well some of the framework in there is a welcomed addition. Less dll’s I have to write.

  18. Jeff: Thanks!

    This is one of the reasons I love working on intellisense (As opposed to just a straight compiler). The enormous amount of fault tolerance that we need to have to support the code you have in it crappy state means that testing and verifying "correctness" is enormously challenging. Even defining what "correct" is is too difficult for me to do. I’d like to blog about this fact alone at some point.

  19. Radu Grigore says:

    Cyrus, I’m not exactly sure why you felt like repeating two times for me what the problem is, but I appreciate your patience :)

  20. Now that .NET 2 supports friend-assemblies, Shouldn’t all the public-but-not-intended-to-be-used members in the framework now be marked Obsolete and replaced with internal versions?

    Either that, or clean up the code, make the members fully public and not hidden, and write some good documentation.

    I don’t see any in-between visibility. I understand the need for internal across assemblies, and while some argue that internal should never be used, I’m certainly not one of them.

    If people are actually using these methods (and I know they are, as I’ve seen several Internet posts about using certain unsupported public classes), and there are good reasons for them not to use them, then mark them Obsolete and give them a chance to stop using them.

    I at least hope that these not-so-public members have been tested for security flaws.

  21. Mark Levison says:

    Sounds like the attribute EditorBrowsableState.Never needs to be deprecated. If you add another state to Intellisense that will only **rarely** appear then I think as end user I will just be confused. In fact knowing me I will spend hours googling ‘intellisense grey’.

    Short of deprecating EditorBrowsableState.Never I don’t see a clean solution.

  22. Matthew W. Jackson says:

    Mark: I can think of at least one case where I’d use EditorBrowsableState.Never.

    If I were writing a Complex struct, I might want to provide as many overloaded operators as possible to as many different languages. If you follow CLS specs, it shouldn’t be a problem if a language doesn’t support all operators. If you write overloaded operators you’re supposed to have alternate methods (usually well-named static methods) for non-operator-overloading languages. In the case of the exponent operator, I would most likely have a static Complex.Pow(Complex, Complex) method so that C# users could call operation. However, VB are able to use ^, so in VB I can have a Public Shared Complex Operator ^(Complex, Complex)

    However, it appears that under the current implementation, the method for this operator shows up in C# Intellisense, even though I’m providing an alternative. Using the attribute, the method should be able to be hidden from intellisense, although for me it still shows up (I don’t know why).

    Speaking of which, if I were to be writing the Complex struct in C# (which I most likely would, being a C# programmer), there is currently no way I know of to mark the op_Exponent method as SpecialName so that VB can recognize it as an operator and consume it. I’ve posted this problem on the MSDN Feedback system, and it seems it has been turned over to the VB team for review (although I’m quite certain that it’s a C# problem).

    http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=3d5f27e5-bfcb-45f4-84fd-b56323910906

    Maybe now I need to post another feedback report since EditorBrowsableState.Never isn’t working in C# (it does work in VB, however).