Archive for the ‘memo’ Category

A quote to remember

Wednesday, October 17th, 2007

I'm not going to do this often but I just found out who said one sen­tence that I just adore and I wanted to store it here for fur­ther reference.

As far as the cus­tomer is con­cerned, the Inter­face is the product.

Jef Raskin

This is so true, and yet too often user expe­ri­ence is so low on project's pri­or­ity list. There's another quote on this:

Users do not care about what is inside the box, as long as the box does what they need done.
Jef Raskin

 

Tech­no­rati Tags: , , ,

Fun with ?: operator

Thursday, July 12th, 2007

First of all, take a look at the fol­low­ing code:

        private string _targetText;
        private int _maxLines;
        private int _maxSize;
 
        public int Lines
        {
            get
            {
                if (_targetText == null)
                    return 0;
                return _targetText.Split('\n').Length;
            }
        }
 
        public bool IsValid
        {
            get
            {
                return  _maxSize == 0 ?
                    true :
                    Size <= _maxSize
                    &&
                    _maxLines == 0 ?
                    true :
                    Lines <= _maxLines;
            }
        }

It's fairly sim­ple, the most impor­tant piece is IsValid prop­erty, that checks if _targetText meets cer­tain length and num­ber of lines limitations.

Now, let's say that  max size is 5, max lines is 1 and tar­get text is "Some incred­i­bly long piece of text". Mil­lion dol­lar ques­tion is: What would IsValid return for these parameters?

 

It would actu­ally  return true, because there is a sub­tle bug in this code. It may not be appar­ent and and it's a tricky beast because you have to know how to look at it to see what's actu­ally going on. The rea­son why it returns true, when all signs on earth and in heaven say it should return false is oper­a­tors pri­or­ity, and the way how ?: gets trans­lated by com­piler to some other code.

Log­i­cally think­ing we would expect the code to exam­ine if max­Size is 0 and if it is to set left hand flag to true, and if it's not zero to set it to whether or not Size is less or equal max size, then to do sim­i­lar thing with max­Lines and Lines and set right hand flag, and then, if both flags are true to return true, and false oth­er­wise. By think­ing this way we assume that it will first run both ?: oper­a­tors and then && the results, in other words, we assume that ?: oper­a­tor has higher pri­or­ity than && oper­a­tor that turns not to be true.

That's because peo­ple think of ?: oper­a­tor like short­hand of if else, whereas mighty Reflec­tor reveals its true nature to be dif­fer­ent. When we com­pile code above and then open it in Reflec­tor we'll see code like this (line breaks and indents added to make it eas­ier to read).

        public bool IsValid
        {
            get
            {
                return 
                    (
                        (this._maxSize == 0) 
                        || 
                        (
                            (
                                (this.Size <= this._maxSize) 
                                && 
                                (this._maxLines == 0)
                            ) 
                            || 
                            (this.Lines <= this._maxLines)
                        )
                    );
            }
        }

I sus­pect that this code looks slightly dif­fer­ent than what you expected. No if else only log­i­cal ands and ors. And it's the rea­son for that unex­pected out­put. If you exam­ine that code closely you'll notice that no mater size and max size — if num­ber of lines is not greater than max­Lines it will return true. So how to fix that code? Either by sur­round­ing ?: oper­a­tors in brack­ets, or by mov­ing them to other properties/methods like this:

        public bool IsValid
        {
            get
            {
                return HasValidSize
                    && HasValidLineCount;
            }
        }
 
        public bool HasValidLineCount
        {
            get
            {
                return _maxLines == 0 ?
                    true :
                    Lines <= _maxLines;
            }
        }
 
        public bool HasValidSize
        {
            get
            {
                return _maxSize == 0 ?
                    true :
                    Size <= _maxSize;
            }
        }

I hope that was informative.

Updating Controls in Windows Forms

Saturday, June 9th, 2007

How often do you find your­self writ­ing code like this:

string[] files = GetFiles(path);

filesListView.BeginUpdate();

for (int i = 0; i < files.Length; i++)

{

    //possibly something more

    filesListView.Items.Add(new ListViewItem(files[i]));

}

filesListView.EndUpdate();

You sus­pend con­trol with Begin­Up­date() in order not to repaint itself with every change you make, then do sev­eral updates to it, and then you let go of it, with EndUp­date(), so that it could repaint itself to reflect all the changes. Easy and sim­ple isn't it? So, what's the prob­lem? I can see at least two:

  1. Read­abil­ity
  2. It's error prone.

I cre­ated only very few lines of code for this exam­ple, but you have to look into the code to find the por­tion between Begin­Up­date, and EndUp­date. It's not appar­ent at the first glimpse of an eye, as it should be. I think that you should be able to imme­di­atelly tell, what por­tion of the code hap­pens when the con­trol is sus­pended, and what not. With this code, you first have to parse it to find Begin­Up­date(), and then match­ing EndUpdate.

And hey, have you ever for­got­ten to add EndUp­date, at the end of sim­i­lar code snip­pet? It, com­piles, you don't even get a warn­ing, but it works far from what you would like it to.

To over­come those con­straints I decided  to cre­ate a sim­ple helper class that would allow me to change code above to some­thing like:

string[] files = GetFiles(path);

using (new Update<ListView>(filesListView))

{

    for (int i = 0; i < files.Length; i++)

    {

        //possibly something more

        filesListView.Items.Add(new ListViewItem(files[i]));

    }

}

//do something else if you need to

Isn't it much cleaner? Imme­di­ately from look­ing at the code you can see which por­tion is in Update block. You also don't have to worry

that  you for­get to call EndUp­date() — it will be done for you.

If you ever played with Win­Forms con­trols, you cer­tainly noticed that not every con­trol has Begin/EndUpdate pair of meth­ods. I never actu­ally checked, but I assumed that for those who do have them, those meth­ods come from some base class, or maybe inter­face. I was greatly sur­prised to dis­cover, that nei­ther of those assump­tions was true. There sim­ply are con­trols that do have those meth­ods, and those that do not, period. I guess that's not the best solu­tion, and there should be and inter­face like IUp­date­able, ISup­port­sUp­date or something.

Lack of that inter­face made me use Update<T> where T:Control instead of Update<T> where T:IUpdateable, so now you can pass in any con­trol, even one, that has no Begin/EndUpdate meth­ods implemented.

So what now? Throw NotIm­ple­ment­edEx­cep­tion? That was my ini­tial thought, but it means that every­thing would com­pile, and you would get excep­tion dur­ing run­time which is some­thing I really dis­like. This leaves us with another option: if given con­trol doesn't imple­ment said meth­ods do noth­ing. It may seem the most rea­son­able option but it promises con­sis­tent behav­ior, and doesn't pro­vide it.

Imag­ine code like this:

using (new Update<Label>(myLabel))

{

    for (int i = 0; i < 10; i++)

    {

        myLabel.Text += i.ToString();

        Application.DoEvents();

        Thread.Sleep(500);

    }

}

Label, obvi­ously doesn't have Begin/EndUpdate, but it's a Con­trol and this code would com­pile. Some­one would expect that for 5 sec­onds label's text stays unchanged, and after that it updates, all the changes in one go. Well, he/she would be bit­terly dis­ap­pointed by the out­come, since instead of one change, we would have 10 of them. It's not what we wanted, it's not what we ought to have, since it means that we get incon­sis­tent behavior.

So what now? I poke around a lit­tle bit with Reflec­tor, and I dis­cov­ered, that Con­trol class has inter­nal method Begin­Up­dateIn­ter­nal, and con­se­quently EndUp­dateIn­ter­nal. That's what we wanted! Althought it's a hacks, because we need to use reflec­tion to call inter­nal method from out­side of its assem­bly it's the best option we have in this sit­u­a­tion. Now we can call pub­lic meth­ods for con­trols that have them, and Control's inter­nal classes for other ones.

Still, I'm not com­pletely sat­is­fied with this solu­tion. I shouldn't be able to do it for Label, TextBox or any other con­trol that is not meant to be updated this way, but to do this, all those con­trols that do sup­port this behav­ior should all be marked with Inter­face, and that's some­thing that only BCL team can do.

Final code for Update<T> looks like this:

public class Update<T> : IDisposable where T : Control

{

    private T _control;

    private Type _type;

    private bool _hasPublicUpdate = false;

 

    public Update(T control)

    {

        _type = control.GetType();

        MethodInfo beginUpdate = _type.GetMethod("BeginUpdate");

        if (beginUpdate == null)

        {

            _type = typeof(System.Windows.Forms.Control);

            beginUpdate = _type.GetMethod("BeginUpdateInternal", BindingFlags.NonPublic | BindingFlags.Instance);

        }

        else

        {

            _hasPublicUpdate = true;

        }

        _control = control;

        beginUpdate.Invoke(_control, null);

    }

 

    public void Dispose()

    {

        MethodInfo endUpdate = null;

        if (_hasPublicUpdate)

        {

            endUpdate = _type.GetMethod("EndUpdate");

            endUpdate.Invoke(_control, null);

        }

        else

        {

            endUpdate = _type.GetMethod("EndUpdateInternal", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(bool) }, new ParameterModifier[] { new ParameterModifier(1) });

            endUpdate.Invoke(_control, new object[] { true });

        }

        _control = null;

        GC.SuppressFinalize(this);

    }

}