XAML makes a lot of things easier. Technically, everything you can do in XAML can also be done in other UI platforms, but XAML just makes it so simple that you're more likely to do them. What this also means is that we need to think a bit differently when it comes to designing our UIs.
Gradients in the Real World
First, I'll start by saying that I am not a designer; most of these tips have come people way smarter than I am. Personally, I like gradients -- probably a little bit too much. They have the ability to make some things look more natural.
There are no solid colors in the real world. Think about this for a moment. There are a lot of colors that we perceive as being solid, but when we really look at them, there is some sort of variation (from top to bottom, from side to side, or from some arbitrary angle) based on how the light hits the object. Because of this, we can add gradients to our applications to make them look more natural; they can also add a bit of visual interest as well.
Gradients in XAML
XAML makes it very easy to add a gradient. All we need to do is create a gradient brush, and we have a couple of options for that: LinearGradientBrush and RadialGradientBrush. The LinearGradientBrush lets us create gradients that move evenly from top to bottom, from left ot right, or from one side to another at whatever angle we choose. The RadialGradientBrush starts at a point and radiates outward (you can think about this as the highlight that you see on the surface of a sphere).
When you create the gradient brush, you specify the gradient stops. This is how you let XAML know where each color should be along the continuum. If you've taken a look at my samples, you know that I have a particular gradient background that I always use. (I'm not recommending this. It's something that I'm used to that doesn't look too bad.)
Here's the gradient definition:
And what it looks like:
What this means: the start point is the starting point for our gradient. In this case, we are using point "0,0" which is at the top-right of the control. The end point "1,1" specifies the bottom-left corner. So, we will end up with a diagonal gradient. Next we have 2 or more gradient stops. The offset value is a value between 0 and 1 to specify where the color should be. In this case, the first gradient stop is at "0", meaning put Alice Blue at the beginning of the gradient (in this case, the top-right corner). The second gradient stop is at "0.7" which you can translate as 70% across the gradient. This point should be Steel Blue. Since we do not have any additional gradient stops, this color will be "solid" through the end of the gradient.
The great thing about specifying gradients in XAML is that we just specify the gradient stops (where we want our "solid" colors to be). The XAML renderer figures out how to make the smooth transition from one color to the next.
Gradients in the UI
Here's another example of an application that uses gradients as control backgrounds. Note: this is an actual application that I wrote and deployed (but the names have been changed to protect the innocent).
Here you can see gradients in the header, the client area, and the login box. One thing to note about these gradients is that they move from one color to a similar color. In the case of the client area, it moves from "Ivory" to "Moccasin" and back to "Ivory". The login box moves from "Steel Blue" to "Dark Blue" and back to "Steel Blue".
Here is the gradient that's used for the header and login box:
This is a little different from our previous gradient. First, the start point and end point specify that this will be a gradient that goes from top to bottom. This time we have 3 gradient stops. The first one specifies Steel Blue at the start point. The second specifies Dark Blue at the 90% mark. The third goes back to Steel Blue at the 100% mark. We can see this as the color moves gradually from Steel Blue (the lighter blue) to Dark Blue, and then quickly back to Steel Blue.
If we were to move from "Steel Blue" to "Orange", things would look a little strange. Think about the gradients that we see in nature. We see subtle differences in colors -- moving from white to gray, or from lemon yellow to a darker yellow. If we create a jarring gradient, then it has the same effect as a rainbow wig -- attention getting, jarring, and unnatural.
Subtler Gradients
In addition to using gradients for control backgrounds, we can also use them more subtly. Take a look at the following screenshot:
Can you tell how this differs from the screenshot above? If you're anything like me, you can see a difference and maybe have a preference, but you have difficulty pointing out what the difference is. Here's are side-by-side comparisons of the affected area:
The difference is that we applied a gradient to the text of the application name. The great thing about XAML is that it makes this incredibly easy. In fact, for this sample, we're using the same gradient for the header, the login box, and the application name. This gradient is stored as an application resource in the App.xaml file. This makes it easy to share across our screens (and also very easy to update if we change our design).
We'll be looking at this application in a little more detail in future posts. At that point, we'll see a few more cool XAML features and how it can add value to our UI.
Happy Coding!
Tuesday, March 13, 2012
Monday, March 5, 2012
San Diego .NET Developers Group
For those of you in the San Diego area, I'll be speaking at the San Diego .NET Developers Group tomorrow night (March 6th). Details are available here: San Diego .NET Developers Group.
The topic is IEnumerable, ISaveable, IDontGetIt: Understanding .NET Interfaces. We'll take a look at what it means to program to an abstraction rather than a concrete class, see how interfaces can help us accomplish that, and then review some real life examples of how we can use interfaces to make our code more robust and extensible. Hope to see you there.
Happy Coding!
The topic is IEnumerable, ISaveable, IDontGetIt: Understanding .NET Interfaces. We'll take a look at what it means to program to an abstraction rather than a concrete class, see how interfaces can help us accomplish that, and then review some real life examples of how we can use interfaces to make our code more robust and extensible. Hope to see you there.
Happy Coding!
Web Service Proxy Collection Types
SOAP-based web services are self-documenting. This means that tools like Visual Studio can create a proxy class for us based on the service description (generally with WSDL -- the web service definition language).
Collection Types
SOAP web services are designed to be interoperable. This means that we can consume a SOAP web service with whatever development environment we are using (.NET, Java, PHP, etc.) regardless of the language that the web service itself is written in. (The converse is also true - we can write our .NET web services so that they can be easily consumed by other development environments.)
When talking about interoperability, this usually means that we are coding to the lowest common denominator -- meaning that we use the constructs that can be understood by the most development environments. For collections in .NET, we have a tendency to use a generic list such as List<T>. But when thinking about interoperability, we need to consider that generic lists do not exist in every environment, and so we take a step down to an Array object.
When consuming web services, we run into the same issue. The service may expose an Array object as a return type. But we would much rather use a generic list in our code. So, what do we do?
Did You Know? Advanced Proxy Settings
The Visual Studio service proxy generator actually gives us control over the types of collections that are exposed by our proxy. When you select "Add Service Reference" for a project, the resulting dialog has an "Advanced" button. If we click this, we get the following screen:
If you have already created a service reference, you can also get to this screen by right-clicking on the service reference in the Project Explorer and selecting "Configure Service Reference".
Notice that there is a setting for "Collection Type". The default for this will vary depending on the environment and type of service that we are consuming. For example, the sample above is a SOAP 1.1 service, and so it has defaulted to the Array collection type (System.Array). But we can change this with the drop-down:
This lets us change from Array to List<T> without having to do any casting or conversion in our code. All of this is handled for us in the proxy that Visual Studio creates for us. Any Arrays that are specified in the service (either as parameters or return types) will be converted to generic lists automatically. Pretty cool, huh?
You can see that we have a number of options to choose from. The List<T> (System.Collections.Generic.List) is just one of them. We could also use an ObservableCollection (which has special features in the XAML world) or one of the other types that meets our needs.
If the services are using Dictionaries, there is a similar option to choose the specific dictionary type -- whether a sorted list, generic dictionary, hashtable, or something else.
SOAP Services Are Still Cool
SOAP services have been getting some bad publicity lately ("They are too verbose") as people move toward REST services. But like everything else in the programming world, there is room for both. Each have advantages and disadvantages; it's our job as developers to weigh those benefits and costs and determine which is appropriate for our situation.
The big advantage to SOAP services is that they are self-describing. This means that we can use tools such as Visual Studio to create a proxy class that makes it extremely easy for us to interact with the service. (Most other development environment have this same sort of WSDL importer tool.)
Visual Studio is a great development environment that gives us quite a bit of control over how the SOAP service proxies are generated. We've seen how we can control the collection types; you can take a look at the "Advanced" features to see what other options are available.
Happy Coding!
Collection Types
SOAP web services are designed to be interoperable. This means that we can consume a SOAP web service with whatever development environment we are using (.NET, Java, PHP, etc.) regardless of the language that the web service itself is written in. (The converse is also true - we can write our .NET web services so that they can be easily consumed by other development environments.)
When talking about interoperability, this usually means that we are coding to the lowest common denominator -- meaning that we use the constructs that can be understood by the most development environments. For collections in .NET, we have a tendency to use a generic list such as List<T>. But when thinking about interoperability, we need to consider that generic lists do not exist in every environment, and so we take a step down to an Array object.
When consuming web services, we run into the same issue. The service may expose an Array object as a return type. But we would much rather use a generic list in our code. So, what do we do?
Did You Know? Advanced Proxy Settings
The Visual Studio service proxy generator actually gives us control over the types of collections that are exposed by our proxy. When you select "Add Service Reference" for a project, the resulting dialog has an "Advanced" button. If we click this, we get the following screen:
Notice that there is a setting for "Collection Type". The default for this will vary depending on the environment and type of service that we are consuming. For example, the sample above is a SOAP 1.1 service, and so it has defaulted to the Array collection type (System.Array). But we can change this with the drop-down:
This lets us change from Array to List<T> without having to do any casting or conversion in our code. All of this is handled for us in the proxy that Visual Studio creates for us. Any Arrays that are specified in the service (either as parameters or return types) will be converted to generic lists automatically. Pretty cool, huh?
You can see that we have a number of options to choose from. The List<T> (System.Collections.Generic.List) is just one of them. We could also use an ObservableCollection (which has special features in the XAML world) or one of the other types that meets our needs.
If the services are using Dictionaries, there is a similar option to choose the specific dictionary type -- whether a sorted list, generic dictionary, hashtable, or something else.
SOAP Services Are Still Cool
SOAP services have been getting some bad publicity lately ("They are too verbose") as people move toward REST services. But like everything else in the programming world, there is room for both. Each have advantages and disadvantages; it's our job as developers to weigh those benefits and costs and determine which is appropriate for our situation.
The big advantage to SOAP services is that they are self-describing. This means that we can use tools such as Visual Studio to create a proxy class that makes it extremely easy for us to interact with the service. (Most other development environment have this same sort of WSDL importer tool.)
Visual Studio is a great development environment that gives us quite a bit of control over how the SOAP service proxies are generated. We've seen how we can control the collection types; you can take a look at the "Advanced" features to see what other options are available.
Happy Coding!
Saturday, February 25, 2012
XAML Namespaces and Default Properties
Here are a few questions that have come up during some recent "XAML: Don't Fear the Markup" presentations.
Question: How can you tell what is in the namespaces that are included by default in the XAML markup?
MSDN and the Visual Studio Help contain listings of the elements that are in the namespaces. For example, the "x:" namespace (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml") is available in this article: XAML Namespace (x:) Language Features. It's interesting to take a quick tour through this list. It shows the "x:Class", "x:Name", and "x:Key" that were mentioned in the presentation; but it also shows things like "x:Null" which are useful for resetting a property that would normally be inherited. For example, if you have a custom Style, you can choose to have that style automatically applied to all of the applicable controls in the XAML document. If a control needs to use the default style rather than the custom style, you can set that control's style to "{x:Null}" to reset the value.
The other namespace that we looked at a little was the "d:" namespace (xmlns:d="http://schemas.microsoft.com/expression/blend/2008"). This also has an article describing the members: Design-Time Attributes in the Silverlight Designer. This shows the "d:DesignHeight" and "d:DesignWidth" that we saw in the sample. It also has some interesting things to look into further such as "d:DataContext" which lets you set up a design-time data context for data binding. In our sample, the databinding was hooked up to a local object, so the design-time view worked without doing anything special. But if you had remote data (for example, customer data coming from a SQL database), then you wouldn't have that data available at design time. In that case, you could use the "d:DataContext" to hook up to a local object that had some sample hard-coded data. These are all design-time functions. They are there to help you with your XAML page/window/user control creation. At run time, your actual data context would be used.
Question: The XAML namespaces use the URI format ("http://..."). How should you name your own namespaces?
When creating your own namespaces, you should follow whatever namespace naming convention that you have in place. There is no need to use the URI format; instead, simply use the standard .NET namespace naming -- usually something like "CompanyName.Product.Module". In the sample, we saw how to bring in a namespace that is local to our project, which ended up looking like this: xmlns:local="clr-namespace:JeremyBytes.StopWatch". But it is fairly simple to bring in namespaces from other assemblies that are referenced in the current project. You can get more information here: XAML Namespaces and Namespace Mapping for WPF XAML.
Question: How can you identify the default property of a XAML element?
The default property of a XAML element is the property that we can simply put "between the tags" without any specific references. In our sample, we saw that we could specify the content property for a button by simply putting the content between the opening and closing tags: "<Button...>Start</Button>". The question is how do we know which property is the default property?
The default properties are specified in the help file. So, all we need to do is press "F1" on any of our controls to see what the value is. Here are a few samples:
Here, we can see what the default properties are for each of these elements. As we saw in the sample, we are always free to explicitly specify the property, but using the default property syntax can help keep our markup more readable.
If you have any questions about the XAML presentation or walkthrough, feel free to send them along to me.
Happy Coding!
MSDN and the Visual Studio Help contain listings of the elements that are in the namespaces. For example, the "x:" namespace (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml") is available in this article: XAML Namespace (x:) Language Features. It's interesting to take a quick tour through this list. It shows the "x:Class", "x:Name", and "x:Key" that were mentioned in the presentation; but it also shows things like "x:Null" which are useful for resetting a property that would normally be inherited. For example, if you have a custom Style, you can choose to have that style automatically applied to all of the applicable controls in the XAML document. If a control needs to use the default style rather than the custom style, you can set that control's style to "{x:Null}" to reset the value.
The other namespace that we looked at a little was the "d:" namespace (xmlns:d="http://schemas.microsoft.com/expression/blend/2008"). This also has an article describing the members: Design-Time Attributes in the Silverlight Designer. This shows the "d:DesignHeight" and "d:DesignWidth" that we saw in the sample. It also has some interesting things to look into further such as "d:DataContext" which lets you set up a design-time data context for data binding. In our sample, the databinding was hooked up to a local object, so the design-time view worked without doing anything special. But if you had remote data (for example, customer data coming from a SQL database), then you wouldn't have that data available at design time. In that case, you could use the "d:DataContext" to hook up to a local object that had some sample hard-coded data. These are all design-time functions. They are there to help you with your XAML page/window/user control creation. At run time, your actual data context would be used.
Question: The XAML namespaces use the URI format ("http://..."). How should you name your own namespaces?
When creating your own namespaces, you should follow whatever namespace naming convention that you have in place. There is no need to use the URI format; instead, simply use the standard .NET namespace naming -- usually something like "CompanyName.Product.Module". In the sample, we saw how to bring in a namespace that is local to our project, which ended up looking like this: xmlns:local="clr-namespace:JeremyBytes.StopWatch". But it is fairly simple to bring in namespaces from other assemblies that are referenced in the current project. You can get more information here: XAML Namespaces and Namespace Mapping for WPF XAML.
Question: How can you identify the default property of a XAML element?
The default property of a XAML element is the property that we can simply put "between the tags" without any specific references. In our sample, we saw that we could specify the content property for a button by simply putting the content between the opening and closing tags: "<Button...>Start</Button>". The question is how do we know which property is the default property?
The default properties are specified in the help file. So, all we need to do is press "F1" on any of our controls to see what the value is. Here are a few samples:
Here, we can see what the default properties are for each of these elements. As we saw in the sample, we are always free to explicitly specify the property, but using the default property syntax can help keep our markup more readable.
If you have any questions about the XAML presentation or walkthrough, feel free to send them along to me.
Happy Coding!
Monday, February 20, 2012
San Diego .NET UI Developers SIG
For those of you in the San Diego area, I'll be speaking at the San Diego .NET UI Developers SIG tomorrow night (February 21, 2012). Details are available here: San Diego .NET User Group.
The topic is XAML: Don't Fear the Markup. We'll be getting comfortable with the XAML markup and see how we can tweak it to meet our UI needs. In addition, we'll take a look at a few cool design tricks that we can use to add a little "glow" to our UI. Hope to see you there.
Happy Coding!
The topic is XAML: Don't Fear the Markup. We'll be getting comfortable with the XAML markup and see how we can tweak it to meet our UI needs. In addition, we'll take a look at a few cool design tricks that we can use to add a little "glow" to our UI. Hope to see you there.
Happy Coding!
Sunday, January 1, 2012
Happy New Year!
Another year has gone by, and we have another year to look forward to. Thank you to everyone who attended one of my sessions at a code camp or user group. You are why I keep presenting.
Over the last 2 years, you've come out to see me at 45 sessions across 20 events, with a total of over 1,500 people in attendance (1,510, but who's counting?).
We've got another great year coming up. I've already got several sessions scheduled, and I hope to see you at one in the future.
Happy Coding!
Over the last 2 years, you've come out to see me at 45 sessions across 20 events, with a total of over 1,500 people in attendance (1,510, but who's counting?).
We've got another great year coming up. I've already got several sessions scheduled, and I hope to see you at one in the future.
- Jan 28 & 29 - So Cal Code Camp (Fullerton, CA)
- Mar 6 - San Diego .NET Developers Group (San Diego, CA)
- Mar 20 - Disney .NET Developers Group (Burbank, CA)
- Mar 21 - San Gabriel Valley .NET Developers Group (Monrovia, CA)
- Apr 26 - Southern Nevada's .NET User Group (Las Vegas, NV)
- May 8 - Inland Empire .NET User's Group (Riverside, CA)
Happy Coding!
Sunday, November 13, 2011
Exceptions in Multi-Cast Delegates
At the last 2 code camps (So Cal Code Camp and Desert Code Camp), the same question came up. How do exceptions affect the process flow when invoking a multi-cast delegate? Let's do a quick review and then take a closer look. For more information, please take a look at Get Func<>-y: Delegates in .NET.
Multi-Cast Delegates
All delegates in .NET are multi-cast delegates. This means that we can assign multiple methods to the same delegate variable. When we invoke the delegate variable (to run the methods), then all methods that have been assigned are run.
For example, take a look at this Action variable:
Action<List<Person>> act;
Then we can assign multiple methods as follows (in this case using lambda expressions):
act += p => Console.WriteLine(p.Average(r => r.Rating)).ToString();
act += p => Console.WriteLine(p.Min(s => s.StartDate));
Then we can invoke the delegate (assuming that "people" is a list of Person objects):
act(people);
The resulting output to the console window is as follows:
6.4285714285714288
10/17/1975
Single-Threaded Execution
.NET does not do anything magical when it runs the methods assigned to the delegate. They simply run in order, synchronously, on the same thread where the delegate is invoked. In the examples from the "Get Func<>-y" session, we invoke the delegate from the UI thread of a WPF application, and we see that if one of the methods is blocking (such as a MessageBox.Show() method that waits for user input), then the other methods do not execute until that method has completed. (As you can imagine, if we wanted the methods to run on separate threads, we could do that; but that is not the default behavior.)
Exceptions
Now, what happens if we add an exception into the mix:
act += p => Console.WriteLine(p.Average(r => r.Rating)).ToString();
act += p => { throw new Exception("Error Here"); };
act += p => Console.WriteLine(p.Min(s => s.StartDate));
What do we expect to happen? Will all 3 methods execute? None of the methods? Only the first one?
The answer turns out to be pretty simple. Since the methods are run in order, the first method will execute successfully, the second method will throw an exception, and the third will not execute. Since we do not have any exception handling defined, our application will stop working. The console window will have the following:
6.4285714285714288
So, we can see that the first method did in fact run, but the third method did not.
Exception Handling
We can keep our application operational by adding a try/catch block around the invocation of the delegate. This will keep our application from completely erroring out:
try
{
act(people);
}
catch (Exception ex)
{
Console.WriteLine("Caught Exception: {0}", ex.Message);
}
And our output now looks like the following:
6.4285714285714288
Caught Exception: Error Here
But something to notice, even though our application continues (and completes normally), the third method still does not run. And this should not be surprising. When an exception is thrown, the system will walk up the call stack until it finds an exception handler. If it does not find one, then the application will error out. In this case, when the exception in the second method is thrown, it walks back up the call stack to the method that invoked the delegate. There it finds the try/catch and handles the exception. But since we exited out of the actual delegate invocation, the third method is not called.
[Update: For a more robust solution, see More Delegate Exception Handling.]
Some Things to Think About
Now let's consider a bit how we use delegates. Generally, we (as developers) are on one end of the delegate or the other -- meaning we are creating a delegate so that other developers can hook into our code, or we are using a delegate to hook into someone else's code.
If we are creating a delegate that we invoke (such as the call to act(people) above), then we should definitely consider wrapping the invocation in a try/catch block. When someone assigns a method to a delegate that we then invoke, we really have no control over what that assigned method does. This means that we need to protect our code to make sure that someone else's mistake does not cause our code to crash. We need to handle any exceptions that are generated by the invocation so that our application can continue to operate (or at least fail gracefully).
Also, as we noted in "Get Func<>-y", we should be checking the delegate before invocation to make sure that it is not null. In this code sample, it would look like the following:
try
{
if(act != null)
act(people);
}...
If we try to invoke a delegate variable that does not have any methods assigned, then we will get a null reference exception. Alternately, we could handle the NullReferenceException in our catch block, but it's usually better to avoid the exception if it can be anticipated.
But what if we're on the other side of the delegate? What if we are a developer who is assigning methods to a delegate to hook into someone else's code? In that case, we need to follow standard practices for error handling in those methods. We could easily add a try/catch block to the method that we assign to a delegate variable. This way we would have the first shot at handling the exception. And since the exception is happening in our code, we are probably in the best position to try to handle it. If we cannot handle the exception, then we can let it bubble up (as we saw in the sample above). How we handle this will depend on the type of delegates that we are working with, what our business processes are, and how we have designed our error handling and failure modes through the rest of the application.
We always need to be on the lookout for the unexpected. In the sample code that shows up in my demos, the error handling has been excluded (just so that we can more clearly focus on the topic at hand). But when building our applications, we need to make sure that we are following good programming practices and making sure that we can handle the exceptions that may occur in our code.
Happy Coding!
Multi-Cast Delegates
All delegates in .NET are multi-cast delegates. This means that we can assign multiple methods to the same delegate variable. When we invoke the delegate variable (to run the methods), then all methods that have been assigned are run.
For example, take a look at this Action variable:
Action<List<Person>> act;
Then we can assign multiple methods as follows (in this case using lambda expressions):
act += p => Console.WriteLine(p.Average(r => r.Rating)).ToString();
act += p => Console.WriteLine(p.Min(s => s.StartDate));
Then we can invoke the delegate (assuming that "people" is a list of Person objects):
act(people);
The resulting output to the console window is as follows:
6.4285714285714288
10/17/1975
Single-Threaded Execution
.NET does not do anything magical when it runs the methods assigned to the delegate. They simply run in order, synchronously, on the same thread where the delegate is invoked. In the examples from the "Get Func<>-y" session, we invoke the delegate from the UI thread of a WPF application, and we see that if one of the methods is blocking (such as a MessageBox.Show() method that waits for user input), then the other methods do not execute until that method has completed. (As you can imagine, if we wanted the methods to run on separate threads, we could do that; but that is not the default behavior.)
Exceptions
Now, what happens if we add an exception into the mix:
act += p => Console.WriteLine(p.Average(r => r.Rating)).ToString();
act += p => { throw new Exception("Error Here"); };
act += p => Console.WriteLine(p.Min(s => s.StartDate));
What do we expect to happen? Will all 3 methods execute? None of the methods? Only the first one?
The answer turns out to be pretty simple. Since the methods are run in order, the first method will execute successfully, the second method will throw an exception, and the third will not execute. Since we do not have any exception handling defined, our application will stop working. The console window will have the following:
6.4285714285714288
So, we can see that the first method did in fact run, but the third method did not.
Exception Handling
We can keep our application operational by adding a try/catch block around the invocation of the delegate. This will keep our application from completely erroring out:
try
{
act(people);
}
catch (Exception ex)
{
Console.WriteLine("Caught Exception: {0}", ex.Message);
}
And our output now looks like the following:
6.4285714285714288
Caught Exception: Error Here
But something to notice, even though our application continues (and completes normally), the third method still does not run. And this should not be surprising. When an exception is thrown, the system will walk up the call stack until it finds an exception handler. If it does not find one, then the application will error out. In this case, when the exception in the second method is thrown, it walks back up the call stack to the method that invoked the delegate. There it finds the try/catch and handles the exception. But since we exited out of the actual delegate invocation, the third method is not called.
[Update: For a more robust solution, see More Delegate Exception Handling.]
Some Things to Think About
Now let's consider a bit how we use delegates. Generally, we (as developers) are on one end of the delegate or the other -- meaning we are creating a delegate so that other developers can hook into our code, or we are using a delegate to hook into someone else's code.
If we are creating a delegate that we invoke (such as the call to act(people) above), then we should definitely consider wrapping the invocation in a try/catch block. When someone assigns a method to a delegate that we then invoke, we really have no control over what that assigned method does. This means that we need to protect our code to make sure that someone else's mistake does not cause our code to crash. We need to handle any exceptions that are generated by the invocation so that our application can continue to operate (or at least fail gracefully).
Also, as we noted in "Get Func<>-y", we should be checking the delegate before invocation to make sure that it is not null. In this code sample, it would look like the following:
try
{
if(act != null)
act(people);
}...
If we try to invoke a delegate variable that does not have any methods assigned, then we will get a null reference exception. Alternately, we could handle the NullReferenceException in our catch block, but it's usually better to avoid the exception if it can be anticipated.
But what if we're on the other side of the delegate? What if we are a developer who is assigning methods to a delegate to hook into someone else's code? In that case, we need to follow standard practices for error handling in those methods. We could easily add a try/catch block to the method that we assign to a delegate variable. This way we would have the first shot at handling the exception. And since the exception is happening in our code, we are probably in the best position to try to handle it. If we cannot handle the exception, then we can let it bubble up (as we saw in the sample above). How we handle this will depend on the type of delegates that we are working with, what our business processes are, and how we have designed our error handling and failure modes through the rest of the application.
We always need to be on the lookout for the unexpected. In the sample code that shows up in my demos, the error handling has been excluded (just so that we can more clearly focus on the topic at hand). But when building our applications, we need to make sure that we are following good programming practices and making sure that we can handle the exceptions that may occur in our code.
Happy Coding!
Subscribe to:
Posts (Atom)











