Friday, November 30, 2007 - 07:59

Events & Inheritance

I came across something odd the other day concerning events and inheritance.

I have a class which inherits from a TextEdit. I created this derived class so that I could add some behaviour to the Click event, without having to hook up the event for every TextEdit I use - so that I could just myTextEdit instead. My first approach was to hook up a OnClick event handler to the Click event in the constructor:


public myTextEdit()
{
InitializeComponent();
Click += new EventHandler(myTextEdit_Click);
}

void myTextEdit_Click(object sender, EventArgs e)
{
// do stuff
}


But while the code hooking up the event was executed, my event handler was never run. So my second approach was to instead override the eventhandler:


protected override void OnClick(EventArgs e)
{
base.OnClick(e);

// do stuff
}

and this worked perfectly.

But I don't understand why the first approach didn't work. While the two approaches are doing radically different things under the covers, they should amount to much the same thing in the end: the event handler should be fired (since it's the base class that's actually raising the event, not the derived class). Maybe I'm just missing something obvious?

I did a bit of reading, and it seems that the overriding is the 'better' way to go, since if you're creating a derived class you're the publisher of the event, and shouldn't be a subscriber. This totally makes sense, but even they seem to think that either method should work. After all, if I’d overridden the base class without touching the events and event handlers, then instantiated my derived class, and then created a 3rd class and hooked up event handlers in that class to the event, I’d expect them to be fired. How does the internal event firing mechanism differ if the event handlers are hooked up in the derived class rather than a 3rd external class?

The next step, of course, was to write a set of test classes to try this out in a simple demo - and just to confuse the issue, both methods worked! So either my demo wasn't set up correctly, or it only occurs on GUI related .NET methods like OnClick(). I'll keep investigating, and report back if I find anything useful... but in the meantime, if you have any ideas, please leave a comment!

Edit: Okay, I think I've figured out what's happening, thanks to a bit of experimenting and this thread. If you declare an event as virtual, the base class and the derived class each have their own delegate list, which is where the problem comes in. If you have an external class which subscribes to the derived class's event, but doesn't subscribe to the base class's event, then trigger the event in the base class, the event isn't fired in the base class (since the delegate is null), and therefore it derived class's event handler can't react to it. I'm not sure if that is what's happening in this case, since the actual event and the base handlers are declared somewhere within the depths of .NET and DevExpress, but it would make sense - and would explain why my simple demo app works perfectly.

Labels:

Sunday, August 26, 2007 - 07:07

Problems With Properties

I've always been a bit suspicious of properties in C#. It just seemed a bit dangerous to be actually calling a method when you're seeming to just be setting a value.

Of course it depends what you use properties for. If you're using it purely to, say, check that the user has permission to set that value, it should be fine. But properties are so useful that they tend to be subject to feature creep. Why not set value A at the same time as you set value B? Since A depends on B, it makes sense to set B in the setter for A; that way they will always stay in synch. But now you're introducing side effects that aren't immediately visible when reading the code.

Here's one that bit me the other day: if A should always be rounded to two decimal places, why not do the rounding in the setter? Then you know it will always be rounded. Well, yes - but then you run into this situation:
myPropertyA = someValue;
myPropertyB = someValue;
if (myPropertyA == myPropertyB)
doSomething();
else
doSomethingElse();
And you find that counterintuitively, doSomethingElse always happens instead of doSomething. Whereas this works as you expect:
myPropertyA = someValue;
myPropertyB = myPropertyA;
if (myPropertyA == myPropertyB)
doSomething();
else
doSomethingElse();
Or at least it does if you're not doing anything odd in the setter. It's also easy to give in to feature creep in getters; after all, does it really matter what the real stored value is if you can control what is provided when that value is asked for? Instead of rounding in the setter, for example, you could round in the getter - so the unrounded value is still available by other means, but most code will see the rounded value. The problem is that it's not clear what you're going to get. If I call a method 'GetRoundedValue()', I know I'll get the rounded value. If I use a property, I think I'm getting the stored value but I might really be getting any manipulation of it.

So I tend to keep my property code basic - provide (or deny) access to values, but don't manipulate those values unless you want to have to trace through every step to see what your code is actually doing.

Labels:

Tuesday, July 17, 2007 - 10:55

Pair Programming Part 2: The First Two Days

So how did my first two days of Pair Programming go? Well, much as I expected, to be honest. I can't say that there's a single point in my first post that I don't still agree with. I'm still trying to keep an open mind, though, since it is early days and none of us really know what we're doing yet. But now that I'm trying it, I can add emphasis and clarification to some of the points that I made before.

Ergonomics in particular is a huge problem; even with sitting in front of a straight desk it just doesn't work. You can move the keyboard and mouse backwards and forwards, but you can't move the monitor. And if the monitor isn't square in front of you, you're going to be sitting slightly twisted, and that ends up hurting your back muscles, and makes it difficult to lean back in your chair and use the support that your chair gives you. And even though you can move the keyboard and mouse, often you just reach for it instead, and that's not really good for your wrists.

I don't know that it's particularly efficient, either. Things will probably improve as we get more used to it, but so far it just seems like a waste of resources. And it's boring! Admittedly, at the moment we're doing fairly simple tasks, that doesn't require two brains, but it means that it's not really taking up a lot of your attention.

And speaking of attention, I do find it difficult to pay attention when I'm not typing. And even when I am typing, I feel that I'm only working at a surface level - I'm not really engaging my brain. And yes, I'm learning stuff since my colleague knows more about this part of the system than I do, but I don't know if I'm really learning it. Learning by doing is important, and learning by watching doesn't work. And even if I drive, I'm not getting enough time to absorb what I'm doing - and I don't have the opportunity to learn by making mistakes, which is even more important! And because I'm not the expert on this section, I feel like I'm not really contributing - in fact, I'm just slowing things down because everything has to be explained. And yes, I know that the XP principles say that I shouldn't feel like that, but I do.

I'm also very aware of the process, rather than the programming. I guess this is partly because it's new, and as we get used to it it will go away, but it stops me from getting really involved in what I'm doing.

I feel that pair programming tries to stamp everyone into the same mould, so that developers become interchangeable. But we're all unique, so it can't work. I agree that from a system point of view, it's good - if someone leaves the company or gets hit by a bus, someone else should be able to take over. But if someone's off sick for a day, is it really necessary that someone else can pick up their code and carry on? And what frustrates me is that everyone customises their PC and their IDE differently. Some things are important, like keyboard shortcuts, and some aren't, like syntax highlighting colours. But if people are interchangeable, and can work with any partner at any PC, you don't get to use all your customisations. And why should we all have to have a work environment that's exactly the same as everyone else, just in the name of redundancy?

There are two things to consider - will it help the company, and will it make me want to come to work or make me want to call in sick? I don't know if it will help the company; maybe, maybe not. But it certainly doesn't make me want to go to work, and while it may seem selfish to think about the effect of paired programming when everyone's interested in doing the best for the company, it's kinda important to me that work is somewhere I want to go.

I don't feel like I'm contributing, and I don't really get any sense of accomplishment when we get something working. I don't get involved in programming like I do when working on my own, I just don't get any sense of flow. And it's draining, being involved in a social situation all day long. And you just can't do anything personal - from checking your mail to clearing your throat, someone is sitting right next to you hearing and seeing everything you do. I know that there is no privacy at work, but this is extreme.

It's a lot like being in meetings all day. You can't do anything personal; you don't really get much done; you have to be polite and friendly all the time; your time just isn't your own.

As I said in my first post - I think a lot of the Agile/XP principles are great. And collaboration is essential - essential for team cohesion, for learning, for enjoyment of your work environment. I'm not saying that everyone should sit in a dark corner and not talk to anyone all day - I'd go insane! But informal, ad-hoc, as needed collaboration is the way to go - forcing me to work with someone all day will also drive me insane.

Labels:

Friday, July 13, 2007 - 05:28

Paired Programming Part 1

The company I work for has decided to try out a more XP approach to programming. We use a lot of XP and Agile techniques already, but they feel it's time to bring it all together, and go to paired programming.

I'm going to blog about this process, because I think it will help me, and might help other people out there too. I don't think they'll mind - I generally don't blog about work, but I'll make sure not to mention them by name, give away any company secrets, or get too much into specifics.

Okay. So to start with, I'm sceptical. I don't see how it will work. The only thing that gives me hope is that the guy in charge of this did it for 2 or so years and says it works great.

So I'm negative, but I don't want to just slam it down. It has been done, and it has been successful, and just because I don't see how it can work doesn't mean it can't work. But not only do I not see how it will work, I don't want to do it. I can see the benefits to the company, and in theory, to an extent, the benefits to developers. But I see huge problems as well, on a developer level. These kinda fall into two categories: practical issues, and development issues. Now maybe some of these have been solved by other teams doing this kind of thing, but these are the issues I see looming.

So let's look at the practical issues. Some may seem silly, but remember that you spend the majority of your waking life at work, so it has to be an environment that you're happy with.

  • Synchronising work times. You and your partner have to work the same hours. You have to have coffee breaks at the same times. You have to have the same lunch times. You have to have bathroom breaks at the same times. And all these breaks have to last the same amount of time for both of you.
  • No personal business. Since every time you do something personal, you're holding up the other person. No phone calls. No browsing. No msn/googletalk/whatever. No email. No email notifications, since you might not be using your PC. And while this might seem like a good idea to management, distractions are useful. They keep us sane, and focused, and human, and in touch with the outside world.
  • No snacking. Not only does it seem somewhat rude to have a biscuit or a chocolate without your partner having some, it's just not practical if you're both working. This can be difficult enough anyway; if you have to sync your lunchtimes too, it becomes really difficult. Get used to being hungry.
  • No personal space. Now you're sharing a desk, you don't have your own desk. You don't have your little family photos. You don't have a space that's just yours. You probably get to use your own chair, but you won't necessarily be using your own keyboard.
  • Physical arrangments. How do you arrange the desks? I hate sitting skew; I have to have the monitor right in front of me. How would that work? Where does the keyboard/mouse live? I have the monitor way closer than most people, and use a slightly bigger font, otherwise I can't see. How will that work? What if different people have different brightness/contrast on their monitors? Different lighting? Different keyboard shortcuts?
  • No music. Can't listen to music on your headphones when you're working with a partner, can you?

And development issues:
  • Who types?
  • Will the non-typing person pay attention? And be able to follow what the typing person's doing?
  • I hate typing with people watching - I end up making mistakes. Same goes for coding - I can do something fine on my own, but with someone watching I get flustered (this post explains it better than I can)
  • I like to take time to internalise a new concept. I make notes that I never use again. How can I manage my learning process to match my partner's? And should I have to?
  • I think better while I'm typing and writing code. I know that's not always right, and in a TDD approach you're supposed to do all that thinking first and coding becomes almost perfunctory thereafter. And maybe that's one of the 'benefits' of paired programming. But it's not how I work, and I don't think you have to do it that way to be a good developer.
  • I don't want to spend all my time teaching my partner. And I don't want to spend all my time learning from my partner, and feeling like I don't have anything to contribute. Both are frustrating and boring.
  • What if one partner (not me!) has a dominant personality? How do you prevent the other person being railroaded?
  • How do you prevent the non-typing partner from switching off, either because they don't understand or just don't care? I've seen the quotes about people who've worked in pairs complaining that after that, when working alone, they felt like they only had a half a brain. But I feel that in a team, sometimes team members only bother to use half a brain, and relies on the other person to be doing the thinking.
  • What if you get a personality clash? (It happens - no-one's fault, but it makes life unpleasant).
  • I just don't like having to talk to someone for every little thing every minute of every day! (I've been reading a lot about XP, and I'm not going to post millions of links. But this one describes my reservations relating to this point really well).
  • I'm not sure that productivity will increase. Each team now has to be twice as productive as an individual, during the same amount of time. Which means you have to do everything twice as fast.
  • I understand the value of everyone knowing every part of the code. But I much prefer having areas of expertise - you have ownership of a bit, you can be proud of it, you feel of value to the team. Have a backup or two, of course - you don't want only one person to know the module, or you'll have problems if they get hit by a bus or leave. But let people feel that they have an area that they feel good about. I know, paired programming proponents will saw that the whole codebase becomes your area of expertise. But it's just not the same.
Communication is good. Collaboration is good. Redundancy (in terms of who knows how much of the code base) is good. A lot of Agile development is good. Working purely on your own sucks. But seeing as developers are often not the most socially adept of people (and I totally include myself in that), and for all the reasons above, I just don't think I can be 100% on board with this.

So while it may work, I don't think it's going to work for me. They're asking us to trust them, and try it for 2 or 3 months, and I'll give them that. It may turn out okay. And if there are any big problems, they're open to sorting them out. But I think going to work is going to get far more painful, fast.

Labels:

Friday, June 29, 2007 - 09:12

Practical Testdriven Development

We never learned much about testing our code when I was at university - the tutors told us about trying out boundary cases and that sort of thing, and for some tutorials we maybe had to draw up a simple test plan or describe how we tested the program; and the testing phase was mentioned in the Software Lifecycle module, but that was about it. No mention of automated testing, unit testing, etc. Partly that was just the way things were done - this was just before Java was starting to be widely accepted, and we were coding in c/c++.

So I always used to test my code manually, trying to run through everything I could think of. Then a couple of months into my 2nd job we decided to start doing unit testing with nUnit, and I hated it. Partly because I'd already written a bunch of code and now had to go back and write unit tests for it, which is always boring. Partly because a lot of the stuff I was doing wasn't really well suited for automated unit testing, being GUI stuff and stuff that was dependent on initial settings on a piece of hardware which I didn't have control over. And partly because because I hadn't designed my code in a way that allowed for easy unit testing. Now, though, I use it as an integral part of my dev process. It's still not quite testdriven, since the idea there is that you design and write your tests, with expected inputs and outputs, before writing your code; I find that difficult to do, because I think best about what I want the code to do while I'm writing, not sitting with a pencil and a piece of paper.

But what I do is write a chunk of code, then go write unit tests. I find that that's when I start thinking about things like, what if this is null? Or, what should actually happen in this case? I use the unit tests not just for automated regression testing, but also an initial way to exercise my code - it's actually than creating a temporary test gui to run each of your methods, which is what I mostly used to do. And then you have the advantage of being able to easily rerun your tests to make sure new code or changes hasn't broken anything.

Another benefit is that your unit tests can serve as an example of what what the code is supposed to do, what kinf of input it's intended to take, and the kind of values it returns. Sure, this should be described in your xml method comments, but sometimes an example just explains it so much more clearly. And if someone needs to know what your code will do in some odd situation, they don't have to wade through the source or hope that your documentation covers that case, they can just look at the unit tests (and hope that they cover that case).

In theory, unit testing is whitebox testing - you should exercise every line, every branch of your code. In practice, I try to do as much as I can but don't worry too much about about it all, since that can be really tricky, and can take more time than it's really worth.

I often don't write any asserts until after I've run the tests, which isn't quite the way it should work, I know. But it's easier to run the test, see what output you get, figure out if it's what you want (and if not, what you do want), and then write the asserts to match. Not exactly the testdriven philosophy, but I find it works well for me.

One of my initial arguments with unit testing was that you shouldn't write unit tests for your own code - if you think of writing a test for something, you've most likely thought of that issue while coding and have dealt with it. It's the things that you haven't thought of that you need to test, and someone else is more likely to see those things than you are. I still think this is true to an extent, but I tend to go into 'test writing mode' where I do think about things that wouldn't have occurred to me while coding - somehow I seem to switch mindsets.

I mentioned above that originally my code wasn't well designed for unit testing, and this is a point that I often find difficult. I don't believe that you should write and design your code with the ease of unit testing in mind; but generally, following good design principles does lead to easily tested code (short, simple methods;
loosely coupled classes, etc). The biggest issue, for me, is private methods. I refuse to make private methods public just so that I can unit test them; and unfortunately you don't have friends in c#. In a way it doesn't matter, since all your private methods must be used some public method at some point, otherwise it needn't exist, so by testing all your public methods you will test your private methods as well. But it makes your unit bigger, and makes it more difficult to test every branch - specifically where you want to vary the inputs to the enclosed private method, in ways that wouldn't come up in normal execution. And while you can use reflection to call your private methods, in practice it's just too clumsy and annoying to set up to do it foe every single method. I don't have a solution for this one yet.

Update: There's a good discussion about testing private methods here, although there is a shorter way to use reflection to invoke private methods, described here - basically, get the type of the class you want to test, call GetMethod() on the type to get the relevant MethodInfo, then call Invoke on the MethodInfo. Simple.

Update (29/06/2007): I came across a very good discussion on 'designing for tests' vs 'testing what's designed', and whether you should make methods public just so that they can be tested.

I've also started working with RhinoMocks, a mocking framework. This helps to keep your tests modular, and helps with some of the issues I've mentioned above. If you're testing methods in Class A, and they call something in Class B, you just mock out Class B telling it what you expect in return when you pass in a specific value. Sometimes this seems like you end up duplicating tests: you could just write a test that calls Class A's method, which calls Class B, and check you get the right value, and avoid writing tests on both Class A and Class B. But that's merging units, and you should test the two issues separately - testing Class A's reaction to what Class B returns, and testing what Class B generates based on the input from Class A, are two different things.

Of course, you're actually testing Class A's reaction to what you think Class B will return - if Class B was written by someone else, it may not return values you would expect (you may think it returns an empty string rather than null, for example). And that's where using Class B instead of a mock of Class B would reveal issues. On the other hand, is that what a unit test should be testing? Or does it rather belong in a higher level integration test?

RhinoMocks also lets you perform interaction based testing, as opposed to just state based testing. In other words, instead of checking what's returned, you can also check that certain methods, in various mocked classes, were or weren't called during the test. This is obviously pretty powerful, but can also get really complex. Especially when you're coming from a state based testing mindset.

I haven't quite got the hang of mocking frameworks yet - they seem like a really cool idea, but in practice, for non-trivial examples, I find it's often quite tricky. Sometimes it seems easier to just set up a higher level system test using something like Fitnesse. And sometimes that's okay, but sometimes you really need the unit level tests as well.

Labels:

Wednesday, May 16, 2007 - 10:09

The Jigsaw Model of Software Development

Last night I came up with (what I think is) a pretty good analogy for coding. Coding is like a putting together a jigsaw puzzle.

Putting that last bit of code in place that makes the whole system work is just like fitting that last jigsaw puzzle piece in place - you get the same kind of feeling, whether it's a 'wow, cool' or a warm and fuzzy feeling. The difference is that when coding, you're not so much trying to find the piece that fits as making it.

Now, there are two ways to make a piece that fits. You can look at the specs to figure out what shape the piece should be, or you could look at the surrounding pieces and figure out what shape would fit in with them. When you're coding on a single person project, it's fairly easy - you know what the pieces should look like, and if one piece you spent hours on doesn't quite fit, you can change some of the surrounding pieces a little to make it fit. When you're coding on a project with lots of other people, it's more difficult. They might not have created their pieces yet, so you can't use those to create the shape of your piece; you have to use the specs. And you can't really tweak their pieces to make yours fit.

Of course, it's even more difficult when the specs aren't very detailed. Then you only have a vague idea of the necessary shape, and the developer coding the neighbouring piece may have a different interpretation.

It's even more difficult when you don't know the system. Then you're trying to do a puzzle without being able to see the neighbouring pieces, and without seeing the picture on the box.

And if you're really unlucky, you're sometimes not even sure which part of the puzzle you're supposed to be working on, and which part of the puzzle you're actually working on.

Modularity helps, though. If you can build up a chunk of the puzzle, then fit your entire chunk into place, you have fewer unknown neighbouring pieces to worry about. If you have to add two pieces here and a piece there, and you have only a vauge idea of what shape they should be, and the neighbouring pieces aren't in place yet (or they are, but you can't see them properly- i.e. they haven't been explained to you), it's extremely difficult.

Low coupling helps too. Sometimes your piece has to merge so much into another piece that it's more like one piece was torn in half, than two separate pieces.

You also have to consider the picture on the box, not just the shape of the pieces. Think of the picture being the coding style - how do you handle exceptions? Do you cache objects locally? Do you use factories, or do you just 'new' objects? All these things contribute to the picture, and while you may have the right shape, your piece needs to fit into the picture as well. And if you can't see the box lid, and the other pieces haven't been created yet or are obscured by bits of paper of lying on top of them, it can be extremely difficult to get the shape and the picture right.

So. Coding by yourself is like doing a small jigsaw puzzle - difficult at times, but possible. Coding a system with other developers can be like doing a little jigsaw puzzle that fits into a larger one - you get to complete a piece, and together you all make something that none of you could have done alone; or it can be like doing a jigsaw puzzle where you don't know what shape your pieces should be, you can't see the surrounding pieces, you don't know what colour and patterns your pieces should have on top, and you can't see the box lid.

Labels:

Saturday, October 14, 2006 - 22:22

How Private is Private?

Okay, there's a lot of things that that title might refer to, but this post is about the most boring interpretation :-)

I came across an odd line of code the other day, and after doing some investigating I found out something about private members that I never knew. This is in C#, but I would assume it applies in C++, Java, and any other object oriented language as well.

This is what I discovered: any instance of a class can access the private members of any other instance of that class! In other words, this code is fine:

public class MyClass
{
private int myVar;

public void accessPrivate(MyClass instance2)
{
instance2 = 42;
}
}

Thinking about it, it does make sense (I almost said, on reflection :-) - the access modifier applies on the class level, not the instance level. And it could come in useful, for example when overriding .Equals - but I still find it somewhat disturbing. And I suspect that with intellisense, people may be using this feature without even realising it - you see the private member in the drop-down list, and select it, without stopping to think that you shouldn't even be able to see it (yet another reason that intellisense sometimes makes life too easy).

Doing some googling, I found several discussions about this - in summary, though, someone posts saying "isn't this odd", someone else posts saying "yes, but it's useful", and someone else posts saying "it's data-hiding, not security".

Labels:

Sunday, October 01, 2006 - 18:53

Saving Visual Studio Settings

I always customize my Visual Studio settings - colours, keyboard shortcuts, window positions, and all that. It's always been difficult to backup those settings, though, either just to keep them safe, or for if you need to reinstall VS, or if you want to copy those same settings to another installation on another PC. After doing that a lot of times, you tend to start only customizing the major things and leaving the rest as default, which is a bit sad.

I know VS 2005 has the facility to save and restore settings, which is great but not so useful for those of us still using 2003. Which is why I went looking - again - for a way to save settings, and this time I actually found something! Basically it amounts to exporting a couple of registry settings, and copying a couple of files in your app settings directory - the full details are here on SteveHarman.net.

Haven't tried it yet - I backed up the settings on Friday, and will try a restore with my fresh install of VS on Monday. Looks like it should work though.

Labels:

Saturday, June 17, 2006 - 22:29

Resetting a StreamReader

I've been doing a lot of personal posts lately, so here's a techie one for a change.

Firstly, an article on El Reg about why Bill Gates isn't quite the saint that everyone's currently making him out to be.

Secondly, though, a useful piece of code that I'll probably lose if I don't post it here. Sometimes it's necessary to reset a file pointer; you've read a bit of the file, but you need to go back to the beginning again. With most languages it's pretty straightforward, but with C# it depends on what kind of stream you're using. With most streams you can just seek to the beginning of the file, or set the position of the stream to 0. When using a StreamReader, though, you need to do both of those and discard the data that the stream has already read in and is holding in its cache:
fileReader.DiscardBufferedData();
fileReader.BaseStream.Seek(0, SeekOrigin.Begin);
fileReader.BaseStream.Position = 0;

Still pretty simple to do, but it's easy to forget the cache, or just seek to the beginning and forget to set the position to 0.

Labels:

Monday, April 17, 2006 - 15:01

The Development Abstraction Layer

I just read this really great article by Joel Spolsky (which I found a link to on GeekRant). It's about the Development Abstraction Layer, and how to get the most of out programmers. It's something that every programmer should read, every manager should read, and pretty much something that everyone in the IT industry should read. I'm not going to do it justice by trying trying to summarise or get across what it says, because it's written really well - the best thing is to just go read it yourself. I really, really recommend this.

Okay, you probably aren't going to go read it unless I show you why you should, so here are a couple of quotes:
Software is a conversation, between the software developer and the user. But for that conversation to happen requires a lot of work beyond the software development. It takes marketing, yes, but also sales, and public relations, and an office, and a network, and infrastructure, and air conditioning in the office, and customer service, and accounting, and a bunch of other support tasks.

and
Any successful software company is going to consist of a thin layer of developers, creating software, spread across the top of a big abstract administrative organization.

The abstraction exists solely to create the illusion that the daily activities of a programmer (design and writing code, checking in code, debugging, etc.) are all that it takes to create software products and bring them to market.

and one of my favourites, because it has a personal resonance for me:
If a programmer somewhere is worrying about a broken chair, or waiting on hold with Dell to order a new computer, the abstraction has sprung a leak.


But just go read the whole thing yourself ;-)

Labels:

Saturday, April 01, 2006 - 23:39

Technical C# Interviews

So back to the world of interviews - things are going well, Company A is drawing up the papers to make an offer; I had an all-day interview with Company B, which seemed to go well; I have an interview with Company C on Monday; and Company D have finally resolved their issues and want to set up a final round interview on Tuesday. Oh, and Company A want to take me out for lunch on Tuesday - somehow I've become a hot commodity, and I'm still quite surprised by it (pleasantly, though).

As I think I mentioned in my last post, most of the interviews I've been to in SA didn't have much of a technical component at all. Here in London, though, technical interviews and technical written tests seem pretty standard (as is an initial phone interview, where they also ask you tech stuff, just to check if you're even worth seeing in person). Most of it's relatively normal stuff, although it can be incredibly difficult to remember syntax and how to do certain things when you're speaking to an interviewer, or writing code in pencil on paper! So it helps to brush up on things that you maybe haven't used in a while, or regularly rely on the MSDN or intellisense to do for you. The things that have come up in my interviews include the following:
  • Dispose & Finalize: why you'd want to implement them, why you wouldn't, an d the pattern for implementing them (i.e. actually writing the code to implement them);
  • The Garbage Collector: basically what it does, how it does it, the advantages and disadvantages of having it
  • Events & Delegates: explaining what they are, why and where they'd be used, and writing code to use them;
  • Hashtables: what they are, how they work internally, creating your own hash codes to ensure they're unique, the efficiency of a hashtable;
  • Searching & Sorting: why you'd want to, how you'd search for a specific item in a sorted list, updating in-place vs updating and resorting;
  • Big-O notation, and the efficiency of various algorithms
  • Attributes: what they do, why you'd use them, how to use them (including custom attributes
  • OO Principles: explaining what polymorphism is, what the 'new' and 'override' keywords do, given a set of of base and derived classes explaining what the output would be of various method calls and why;
  • Multithreading: updating the GUI thread from a subthread, the various thread synchronisation methods, deadlock and how to avoid it, what object to use in the lock statement, how to create and start a new thread, and how to kill a running thread, and how to deal with a deadlocked thread
  • Basic sql queries: explaining the difference between 'where' and 'having', explaining what a query would return, writing simple queries (usually containing a join), the differences between the various types of joins, normalisation

As well as general code stuff like writing a linked list class, or writing a method to reverse or parse a string, or finding all the prime numbers below 1000.

Umm, I'm sure that there's more, but I think that list covers the basic areas. I'm not going to give the answers :P - if you don't actually know the stuff, then looking up answers isn't going to help you even just in the short term, and definitely not in the long term - but if it's stuff you know but isn't sitting at the top of your head, it might be a good idea to go and revise it so that you don't have to struggle to remember during the interview itself.

Of course, there are always the other types of questions too - what are you looking for, what have you done in the past, where do you see yourself in 5 years, can you give an example of a time when you... And those are often more difficult than the tech stuff! I used to cringe when they said, "we've got a written test for you to do", but now I think, "oh good, no more questions for half an hour or so!". In fact, one of the most difficult questions is: "So do you have any questions for me? Because there's so much you want to know, and you don't want to look like you're not interested - but very little of what you want to know can be easily phrased in a question, or easily answered (like, the vibe of the company - what's it like to work here? Or, so what will I actually be doing every day?) and a lot of it, while important, is difficult to ask without giving the wrong impression (what are the working hours? and not just the official ones, but the ones that people actually have to work? how much leave do I get? and, most importantly, how much are you going to pay me?). Most of these get answered along the way, but it's still very tricky all round.

So that's my take on interviews - I feel like I'm kinda an expert at this point :-) Of course this isn't complete - there's all that normal advice that they always give you, like be yourself, be enthusiastic, look interested, dress appropriately... and so on and so on. But that's standard interview stuff, and I'd guess most people know all that already.

Labels:

Saturday, March 25, 2006 - 13:37

Attributes

No, I didn't really write up 2 posts today - I don't have internet access at my flat at the moment, so I've been blogging offline and then posting at a local internet cafe, which kinda sucks. Sadly, the broadband stuff arrived today, and I was really excited - but it turns out that the landlord has to set up on his laptop, I'm not really sure why, and he can only do that next Saturday. So that'll be another entire week without internet, and now I'm really disappointed. I actually spoke more to my friends here in London when I was in SA, than now that I'm actually in the same city as them. (Yeah, I could just phone them - but I really, really hate using the phone. It's like a phobia, almost).

Anway. On to the next installment of "C# for Interviews". This time, we're dealing with Attributes.

Attributes
Okay, a quick rundown on attributes. Attributes can be added to methods, classes, properties, fields - just about anything, really. It's way to add extra information about the item to the metadata of the assembly, and there's a bunch of predefined attributes (such as 'Serializable') or you can create your own.

Creating your own attributes is pretty simple; the most complex part is determining that an attribute is the best way to go. I haven't used them much; one case where they came in useful was when I had a class with a whole lot of properties, some of which were required and some of which weren't. I tagged each property with a 'Required' attribute, and when the class was populated I'd send it off to a Validator which would check the value of the 'Required' attribute, then check the value of the property to see if it was set.

So let's say we're going to do this. The first step is to create a RequiredAttribute class:


// you can set what language elements (methods, fields, etc)
// you want the attribute to be used for
[AttributeUsage(AttributeTargets.All)]
public class RequiredAttribute : System.Attribute
{
 //this should be private, and we should use properties to access it
 //but for this example, let's just make it public
 public bool Required = false;

 public RequiredAttribute(bool isRequired)
 {
  Required = isRequired;
 }
}


You get named and positional parameters, but we'll just use positional ones for this example.

Now we can tag an item with this attribute::

class MyClass
{
  [Required(true)]
  private int numRows;
}


Note that we could either use 'Required' or 'RequiredAttribute', they'll both work.

Now that we've said that numRows is required, how do we check the value of this attribute in another class? Using reflection, of course. To get a list of all the custom attributes and their values, you can do something like this:

System.Reflection.MemberInfo info = typeof(MyClass);
foreach (object attribute in info.GetCustomAttributes(true))
{
// test the type of the attribute, and deal with it appropriately
}


But this isn't quite what we want to do with the RequiredAttribute. We want to test the value for a particular field:

foreach(FieldInfo field in (typeof(MyClass)).GetFields)
{
 foreach (object attribute in field.GetCustomAttributes(true))
 {
   if (attribute is RequiredAttribute)
   {
    return ((RequiredAttribute)attribute).Required;
   }
 }
}


And it's as simple as that. You get the type of the class, get the fields (or methods, or whatever) belonging to that class, get the customattributes for each field/method/whatever, find the attribute of the correct type, then cast it and get the relevant value.

Labels:

Delegates & Events

So at my interview the day after my last post, the first question was: implement the IDispose pattern :-) So that worked out pretty well, although I did forget that the Finalize method uses the C++ Destructor syntax, and isn't a method named Finalize - which was pretty dumb.

The next question, though, was on delegates and events, and this is one that I always forget the syntax for - I tend to look it up in the MSDN when I need to use it, and then forget about it until next time. So to cement it in my head, and because it's actually quite tricky to find in the MSDN since there's so much stuff on Delegates and Events, here's a quick summary.

Delegates On Their Own

Basically, a delegate is a reference to a method. They can be used on their own, or more often, in conjunction with events. Delegates are really simple to declare:

// put this somewhere in your namespace
public delegate void MyDelegate(Object myParam);


Now if you're going to use this delegate to get one class to call methods belonging to another class (i.e. not using events), you'll need a method in that class that takes a delegate as a parameter:

// in your calling class
public void DoProcessing(MyDelegate processMethod)
{
Object obj;
// do something to populate the object
processMethod(obj);
}


The processMethod method will then be called in the context of this calling class. It will be declared in your second class:

// in your second class
public void someMethod(object obj)
{
// process the object
}


Then to actually use the delegate:

Caller.DoProcessing(new MyDelegate(c2.someMethod));


Alternatively, you can call the delegate directly:

object obj = "Hello";
MyDelegate md = new MyDelegate(obj);


So that's pretty simple. Using delegates with events is also pretty simple, provided you know how to use events:

Delegates & Events

You can declare your delegate as above, although if you're using it with events you'll probably want it to take an EventArgs parameter (or your own custom EventArgs class, which should then derive from the EventArgs base class). so let's use the MSDN example, of a list class which fires off a Changed event when an item in the list changes:


public delegate void ChangedEventHandler(object sender, EventArgs e);


Now within your class, declare an event of ChangedEventHandler type:

public event ChangedEventHandler Changed;


Now define a method that will fire off the event when an item changes:

protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this, e);
}

And that's all there is to it, really - you'd call this OnChanged method in, say, the Add() or Delete() methods in your list, creating the EventArgs object and populating it as you require.

The only other thing you have to do is hook up the actual method that you want to handle the Changed event, which you'd do when you instantiate your list class:

private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}

// within your main method, or wherever you need this:
List list = new List();
List.Changed += new ChangedEventHandler(ListChanged);


There are some subtleties when inheriting classes with events - events themselves are not inherited, so you need to create a protected invoking method that derived classes can call, something like we did above. Better yet is to declare it as virtual, then the derived class can decide for itself whether to invoke the base event or provide one of its own.

Interestingly, events can be declared in interfaces.

If you are using a delegate which takes only an object 'sender' reference and an EventArgs class, you don't need to declare your own delegate, you can use the default .NET EventHandler delegate, which might save you a line or two of code :-)

So now that you can see how simple it is, go forth and delegate! (well, actually, don't - delegates can make your code very difficult to read and maintain. They have their place, and are sometimes essential, but use them because you need them, not just because you can!).

Update: I came across something odd today this with events. Actually, it's something I should have remembered, because I've come across it before - but I didn't. The point to remember is this: if a class subscribes to an event, and is eventually disposed, it will continue responding to the event until it's actually garbage collected. Counter-intuitive, I know, but that's how it works - so remember to unsubscribe from all events in your dispose method, otherwise you'll end up getting some odd behaviour!

Labels:

Wednesday, March 22, 2006 - 14:08

Finalize, Dispose, and London

I haven't blogged for a while, because I was busy packing up all my stuff and moving to London. But now I've been here for just over a week, have a room in a flat, and am in the middle of job interviews, and recovering from the apparently traditional adjusting-to-London's-climate cold, and I figured that it's about time to say something.

London's cool - more familiar than the States, but different enough not to be boring. I'm starting to find my way around, and at some point in the next week I'll write up my "South African's Guide to London", which just might, possibly, be mildly interesting or vaguely useful to any other South Africans coming over here, specifically ones who don't have a friend here to help them out (luckily, I did :-).

But the next couple of blog entries are going to be dev related - I haven't been coding for over a month, and my skillz are getting a bit rusty, so I've been revising stuff for the tech interviews that I've been going through. And while the MSDN is really useful, I thought it might be useful (to me, at least) to have some of the tricky stuff up here, so that it's easy (for me, at least) to find.
So without further ado, on to Dispose and Finalize.

This is a tricky subject, and I don't really know enough to debate all the ins and outs. Some people say, never implement Finalize, but from what I see in the MSDN you kinda have to - if you have unmanaged resources that you need to clean up, of course, like a database connection. Otherwise you probably shouldn't, since it is pretty slow.

The best way to go about it is to implement IDisposable. This has the advantage that anyone using your class/resource can use the 'using' clause to make sure that Dispose() is automatically called when it goes out of scope, even if an exception is thrown (sure, they could use a try-catch-finally block, but they might not).
So the MSDN-approved pattern is this (at least, in my version of the MSDN):

1. Implement a Dispose() method. This will be called if the instantiater of the class uses the using clause, or whenever they call Dispose() explicity. The Dispose() method should look something like this:
public void Dispose()
{
  Dispose(true);
  GC.SuppressFinalize(this);
}

You want to suppress the garbage collector, since you've already cleaned up everything in the class (and of course, for all the objects it contains - don't forget that bit!).

2. Implement the Dispose(bool) method:
public void Dispose(bool Disposing)
{
  if (Disposing)
  {
     // dispose the managed resources
     // e.g. if you had a Component object, call      //Component.Dispose()
}

 // regardless, clean up your unmanaged resources,
 // like handles or DB connections
}


3. Implement Finalize()
~MyClass()
{
 Dispose(false);
}

That way you cover all your bases - if the user of your class calls Dispose, it'll dispose of everything, otherwise the garbage collector will call Finalize and your unmanaged resources will be cleaned up.

You need to remember that there's no guarantee of the order in which objects will be disposed or finalized, so you shouldn't reference any external objects. And your code shouldn't throw any exceptions, even if Dispose() is called more than once - a good thing to do is have a field called something like 'isDisposed', and then check that in the beginning of your Dispose() method and do nothing if it's true. Equally, you should check the value of isDisposed in every class method, and throw an ObjectDisposedException if it has been disposed already. Also, derived classes should implement their own Dispose(bool) methods, which call that of their base class.

And that's that for Finalize and Dispose. Next time - maybe design patterns, and where to find them? Maybe. Also coming up: Structs vs Classes, Custom Attributes, using SQL in C#, and the other perennial interview favourite, Delegates (both on their own, and together with Events).

Labels:

Thursday, December 29, 2005 - 10:19

C# Express

So I've just installed the new Visual C# Express.

It wasn't as easy as you might think, since I first had to uninstall Beta 2. What they don't mention (although it's fairly logical, if I'd taken just 2 seconds to think about it) is that you should uninstall VS and SQL Server *before* uninstalling the framework. And if you do it the wrong way round, it's then a bad idea to try to just delete the rest of SQL Server manually. Trying to install the new SQL Express on top of this mess doesn't work - nor does reinstalling the old framework and SQL Server! Eventually I did a search on google and found a tool to clean up a SQL Server installation, ran that, reinstalled the old version (which still came up with errors, but luckily nothing that would stop me uninstalling it), uninstalled the old SQL Server, uninstalled the old framework, then reinstalled the new framework, C# Express, and SQL Express.

So after all that, I haven't really had much time to use C# Express, although I did open one of my small VS2005Beta2 solutions and it compiled fine. I have noticed a couple of things, though:
  • SQL Server Express (on the Ready Launch CD, and on the C# Express CD) is just the server. You have to download the equivalent of Enterprise Manager/Query Analyzer (called Microsoft SQL Server Management Studio Express, or something like that) separately, which about another 30Mb :-(
I know that Visual C# Express is a cut-down version of VS2005 "for students and hobbyists", but I didn't (and still don't) know what that really means. Is the IDE a subset of the VS2005 IDE, or is the language/compiler not complete? Visual Studio started the process of confusing the distinction between the language and the IDE - unfortunately the express editions are just making it even worse. I haven't really had time to investigate the language/compiler features (and seeing as 2.0 is new to me, it might be really hard to do that even if I had VS2005 installed to compare to), nor I have I played with the IDE much, but here are a couple of IDE things I noticed right away:
  • fewer customizations options; for example, I can't set whether or not I want to see the start page (as opposed to a blank solution, or my last solution) when I start VS. You can set a lot of visual options (colours, indenting and other formatting), but no project or solution options (such as your default project folder) or international settings. Some of the more advanced compiler options are missing as well.
  • There doesn't seem to be support for source control.
  • You don't seem to be able to attach to a running process.
  • There doesn't seem to be an add-in manager - does this mean that you can't install things like testdriven.net, or compuware's devpartner profiler?
I'm sure there's a lot more missing as well. Quite possibly these are things that a hobbyist or student doesn't need, so I'm not necessarily criticising their absence. But for someone like me who uses the full edition at work, but wants to be able to code their own projects at home using C# Express, it's important to know the differences between them. If only to know the lack of which features will drive you insane :-)

Update:
I've just installed testdriven.net, so I can confirm that it does work with C# Express. Yay! And they've added some cool looking features, like ad-hoc tests (just right-click on a method to test it!).

Update:
I haven't really used C# Express much since I installed it, but lately I've been doing some coding. I can't really comment on .NET 2.0, but from an IDE point of view, I'm ambivalent about C# Express. For one thing, I can't get the colours quite right, but that's just me. I really don't like that you can't edit a file while debugging; sure, there's edit and continue, but you can only do that while execution is paused. What I often do is notice a problem, and leave the program running while I make a quick edit to the source. I don't mind that it will only take effect after I stop execution and recompile; the important thing to me is to get the change made while I remember what I need to change. So that's one thing that bugs me.

The rest of it's okay - better than VS 2003, but worse than VS2003 + resharper. Refactoring really doesn't work well, and there are a bunch of little things that just don't feel quite right. If it weren't for the new features of .NET, I wouldn't bother to upgrade the IDE.

Labels:

Thursday, November 24, 2005 - 21:57

Visual Studio 2005 Ready Launch - Cape Town

So I went to the Visual Studio 2005, SQL Server 2005 and BizTalk Server 2006 Ready Launch at Thunder City today (and yes, they did keep calling it that throughout the presentation, even though it's a bit of a mouthful - and if I hear the word "absolutely" one more time, I'm going to scream).

It was pretty cool - far more of a marketing event than a tech event like DevDays, but the setup was pretty good. It was in the main (empty) hangar at Thunder City, and they had really loud music and fog machines misting up the hangar at first, so it was really impressive. Things quieted down a bit once they started; Ed Jordan was the MC, with some comedian guy (I forget his name), who was really funny at times, but, at other times, not so much.

There's not really a lot to say about the presentations - in fact, I tuned out through most of it as it was very vague wishy-washy marketing hype like "more productive" and "improved peformance" with lots of "integrated" and "interface"s dropped in. So there's probably better comment on this over at dotnet.org.za.

But here are a couple of my thoughts:

In the very beginning, they played a video clip of Steve Ballmer saying how great this launch is. I was half-expecting someone to shout out "Google", or "Hide the chairs!". But this crowd was either too polite or too microsoft oriented to do that, unfortunatley :-(

They played another clip of how HMV absolutely love MS and are using their products for everything including their digital download service. Despite lots of comments about digital downloads, they didn't use the word MP3 once, and the only device they showed playing these digital downloads were not iPods (I think they were the Creative Zen, but I'm not sure). Similarly, BB&D, one of their partners, was running a competition to win MP3 players - again, not an iPod in sight :-)

They made a big deal about how SQL Server Express is now available for free, and you can just download it free. And that Visual Studio Express is also available via a free download for a limited period. I don't think they realised that they're in South Africa - either we're on dial-up, and can't download anything larger than a meg or two; or we're on a hard-capped ADSL line and don't dare download anything large if we want to have any bandwidth left for e-mail and surfing.

From a tech point of view, they showed how integrated SQL Server and Visual Studio have become - Query Analyzer and Enterprise Manager are now integrated into a Visual Studio style app, and - this is cool - you can simply drag and drop stored procs into your code! And can then set breakpoints and debug your stored procs! You can also - and this I have my doubts about - call .Net code from your stored proc. In the demo, they put a call to a webservice into a stored proc, which just doesn't seem like a very good idea to me.

I took some pictures on my cellphone camera, but they all turned out pretty badly. If I get a bunch of comments clamouring for photos, I'll post them, but somehow that seems fairly unlikely :P

Update: I hear that in the US, everyone got CDs containing Visual Studio 2005 Standard Edition - free. We got a 90-day trial version of VS2005 Pro :-( Yeah, that makes a lot of sense - in a First World, large country where exchange rate isn't an issue and where you probably make most of your sales, give away your product free. In a 3rd world developing country where the exchange rate is horrible and which probably isn't a major market for you anyway, hand out 90-day trial CDs. The mind boggles.

But, all is not lost. I mentioned above that you can download SQL Server Express - if you attended the launch and got your 90-day trial of VS 2005 Pro CDs, you have SSE already! Just go to CD2, go to the WCU folder and you'll see a folder named SSE. This seems to contain both the 64-bit and 32-bit editions of SQL Server Express (although you have to uninstall all Betas to be able to install it, so it may not be worth it if you're still using VS2005 Beta 2 - when do these betas expire, anyway?)

And of course these CDs contain the .Net Framework 2.0, which should also save you some bandwidth. The SQL Server 2005 MSDN dvd also seems to contain the msdn not only for SQL Server, but also for VS2005, which should save you even more bandwidth (I'm assuming that while the SQL Server install on the CD is limited to a 180-day trial, the msdn stuff isn't. This may or may not be a reasonable assumption.)

But while they claimed that they couldn't distribute SSE and the VSE editions on CD because it would be too much admin, they did manage to distribute a Ready Launch CD containing, from the look of it, a bunch of marketing brochures as .pdf from themselves and their Launch partners.

What I would have liked to see, instead, is a real breakdown of the differences between the different VS2005 editions. Not the fluffy, hand-wavy "this one's aimed at hobbyists, and this one's for serious developers, and this one's for big companies"; definitive "this includes xxx while this one doesn't" kind of thing.

Labels:

Monday, October 31, 2005 - 19:14

I generally try to not to blog about work, however much I'm tempted or need to vent - it's just not a good idea, for a whole variety of reasons. One thing I am going to say, however: if you have a web app, which connects to a web service, and you end up wanting this web app to look and act just like a normal windows forms app - why not just make it a windows app, and save your developer a whole lot of stress and extra, needless, messy work? I mean, if you want something that looks like a duck, and quacks like a duck, why dress up a goose in a duck costume?

So in an effort to avoid talking about work, or other things that I'd like to talk about but not in a public forum, I thought I'd give a list of what I've been reading lately. I don't really expect this to interest anyone much, I'm really just talking to myself as usual (only more so) :-)

Right now, I'm reading Pride & Prejudice by Jane Austen, since I realised that I've never actually read any Jane Austen and thought it was time that I did. And this was the only one that they had at the library. It's okay - good to read when you're stressed, since all their big worries just seem so small, it's quite amusing.

I'm also reading Grunts! by Mary Gentle, which I've found rather disappointing. I've been wanting to read it for ages, and saw it on special at Kalahari.net snd included it in a giant order of about 6 books. But despite it being billed as a comedy, it's not really funny - the basic premise might be mildly amusing, in that it's a bunch of orcs who start acting like US marines due to the influence of the uniforms and weapons that they find in a dragon's hoard, but the bulk of the book seems to be a lot of battles and people being blown up.

The third (and last) book that I'm currently busy reading is The Borribles, by I don't know (and am too lazy to get up and walk the couple of steps over to my bookca^H^H^H^H chair with books piled on it to check. I don't know if it's just not my sort of book, or if I just wasn't in the right mood last night when I started reading it, but I haven't got past page 3 yet.

Yes, I really am reading 3 books simultaneously :-) As well as the latest issue of Realms of Fantasy.

Books that I've recently finished reading: Do Not Pass Go, by Tim Moore - a view of the history and present of London, as seen from the perspective of the properties of the Monopoly board. It was quite interesting and funny, although all the complaints about how they've torn down all the magnificent old buildings gets a bit boring.

Thud!, by Terry Pratchett - I was really looking forward to this one too, and it was okay but not great. I think this was partly because the edition I got was the US edition, which had a lot of typos as well as a really large font. But I remember that Nightwatch didn't impress me much at first, but when I reread it I enjoyed it a lot more, so maybe this one will be the same. But it struck me as being a cross between Nightwatch and The Fifth Elephant, and not really as new and groundbreaking as the Discworld books normally are.

Hmm, what else have I read recently? Tons, but nothing that really sticks out in my mind. I also bought a bunch of the Penguin birthday books (again, can't remember what they're really called, and too lazy to get up and check), so I carry a couple around with me in my backpack and pull them out whenever I'm bored or go to McDonalds or somewhere for lunch at work. Currently reading "Scenes from Academic Life", "Why The World Went To War", and "In Defence of English Cooking". Oh, and I also recently finished "Frost On My Moustache", also by Tim Moore, another travel type book that's quite funny (I ran out of Bill Bryson). Also re-read a bunch of Tom Holt books (Paint Your Dragon, and Wish You Were Here).

Labels:

Friday, October 21, 2005 - 09:55

Useful SQL script

Here's a really useful script that generates insert statements for all the rows in a table, essentially letting you save your table data to a file. It's great if you want to send the contents of your table to someone else, or as a backup for sample data, or all sorts of things.

http://vyaskn.tripod.com/code.htm#inserts

Looks like there are some other useful scripts on that page too, but I haven't had a chance to look at them yet.

Labels:

Sunday, October 16, 2005 - 00:27

PictureBox in C#2.0 Beta

I'm having an odd problem with PictureBoxes in C# 2.0. Specifically, I'm having a problem assigning them at runtime (not changing their image - that's fine).

This is best illustrated using code snippets.

This works (MapPB and pictureBox1 are both PictureBoxes):

private void button2_Click(object sender, EventArgs e)
{
pictureBox1.ImageLocation = @"D:\Misc\cthulhusmall.jpg";
MapPB.ImageLocation = @"D:\Misc\cthulhusmall.jpg";
}

This doesn't - MapPB just stays empty:
private void button2_Click(object sender, EventArgs e)
{
pictureBox1.ImageLocation = @"D:\Misc\cthulhusmall.jpg";
MapPB = pictureBox1;
}

And the same for this:
private void button2_Click(object sender, EventArgs e)
{
MapPB = pictureBox1;
pictureBox1.ImageLocation = @"D:\Misc\cthulhusmall.jpg";
}
and of course this:
private void button2_Click(object sender, EventArgs e)
{
MapPB = pictureBox1;
pictureBox1.ImageLocation = @"D:\Misc\cthulhusmall.jpg";
MapPB.ImageLocation = @"D:\Misc\cthulhusmall.jpg";
}


It's as if it's okay to change the properties of a PictureBox, but not to assign another PictureBox to it :-S Once you do that, you're screwed, no matter what you try to do to itafterwards. I just can't figure out why.

I've never really used PictureBoxes before, so I don't know if they work the same way in 1.0 (and something to 1 on a Saturday night/Sunday morning, I'm not too eager to write some code to try it out); and I don't know if this is by design in 2.0 or a bug, seeing as it is a beta release.

If you have any ideas or solutions, please leave a comment - this is seriously going to impact the design of my app, if I can't assign PictureBoxes at runtime :-(

Update: as a friend pointed out a couple of minutes after I posted this, of course it won't work, since the control that is drawn is the one in the form's control collection, and not MapPB. So changing the object that MapPB points to is not going to change the control that's drawn.

Labels:

Monday, August 29, 2005 - 14:52

Windows Forms Layouts

I've been working with forms in .net lately, and there are a couple of real deficiencies. I think some of them may have been fixed in 2.0, but unfortunately there are lot of us still using the older version!

The most annoying thing is that you can't zoom in or out of the form that you're editing in the designer. It's fine if you're only creating a small form, but if you're working on a large form you need to keep scrolling around to see the various parts. Sure, you can close some of the extraneous windows (such as the Solution Explorer), and even some of the necessary windows (such as the toolbox), but it's just not enough. Why not have a zoom control like most image editing packages?

It would also be really useful to be able dock or anchor controls to each other, rather than only to the edge of the form. I think that this has improved in 2.0, although maybe not in this exact way. And a less buggy docking system would be great... currently, I'm trying to design a form with various controls on it so that some of the controls (checkedlistboxes) expand horizontally and vertically when the form is maximized. This involves lots and lots of panels, anchoring, and docking! Each control (together with its label) has to go on its own panel, anchored appropriately, and those panels need to be grouped into other panels - each subpanel must be docked correctly within its parent panel. And then depending on how complicated the form is (which isn't very: a simple 2 x 2 layout, maybe a 3 x 2 in some cases), those panels need to be docked within another panel. And then you need a resize event handler for each containing panel, to resize the inner panels appropriately - see this Code Project article for a simple example.

And this is finicky enough, but it's even worse when the panels refuse to dock correctly. The order in which you dock controls is very important, and sometimes not intuitive. In this case, I have three vertical panels - the left should dock to the left, the right to the right, and the middle should fill the available space. But if I dock left to left, then right to right, then middle to fill, the middle panel takes up the left panel's space! No matter what order I set the docking in, it just won't work. And if I dock the middle panel to the right, it docks to the right of the form, not to the left edge of the right-docked panel. In this case, anyway ... in other situations, that's worked okay.

Aarrrgh.

Update: It turns out that while setting the docking in the right order is important, what is even more important is the order in which the controls are instantiated. You know that code generated by the designer that you're not supposed to edit? Well, if your docking isn't working correctly, you may need to edit it. For example, I have a label docked the top of the form, and a datagrid also docked to the top, but I want the label docked above the grid, and the top of the grid essentially docked to the bottom of the label. It was working on one form, but not on the other - the difference was that the grid had to be instantiated *before* the label.

Labels: