A few time ago, one of my colleague has seen the following code
class UsingVariable
{
private int myValue;
public int MyValue
{
get { return myValue; }
set { myValue = value; }
}
public UsingVariable()
{
myValue = 10;
}
}
and proposed us to migrate it to that
class UsingAutoImplementedProperties
{
public int MyValue { get; set; }
public UsingAutoImplementedProperties()
{
MyValue = 10;
}
}
argueing /explaining that now with the .NET 3.5 new features, it was possible, and as the local variable was not usefull, we could get rid of it.
When he told that I had a strongly opposed reaction. We should not do that. The local variable is not usefull ? And what about if the property was harmfull ? Let's see what could happen.
Comparison of the two codes:
Well nothing really to see. The code are currently perfectly equivalent. They will behave the same. Some purist may tell that the second is "slightly" slower (as using a call to property, ie to a method). Well... This is perfectly negligeable.
So these two codes are equivalent. I would precise : these two codes are equivalent... at the condition you assume your code will never get touched and changed in the future. And I'm not sure this is a bet an english bookmaker would do.
Let's see two possible evolutions and check what would happen.
Evolution 1 : some event raising in the property
You all know that for an auto-implemented property, both the get AND the set must be auto-implemented. As a consequence, if you need / want to do some extra treatment in one of the two, you will need to get rid of the auto-implementation to do it yourself. What could happen ? Let's imagine something like :
class UsingAutoImplementedProperties_Evolution1
{
private int myValue;
public int MyValue
{
get { return myValue; }
set
{
if ( myValue != value )
{
OnMyValueChanging(EventArgs.Empty);
myValue = value;
OnMyValueChanged(EventArgs.Empty);
}
}
}
}
We'll simply follow some .NET conventions to raise an event when the property has been changed. Well is that impossible ? Far from that I think.
What may happen ? Well, an event may be raised to indicate to a client that the internal state of the object has been changed. And the client may do some treatment on the object whose internal state has changed. Well yes but on which object really ? On that object I'm manipulating and on which I'm still not finished with the constructor ? Meaning on that object that may have any incorrect or un-initialized values ?
We can just smell there is something going wrong here.
Note that this case may not happen in real life. If I want to handle the event, I will need to have an instance no ? Right. Anyway, this example - not realistic but not so far from real life - let us smell and glimpse there may be some tricky problem to find out there.
Evolution 2 : Adding a level of hierarchy
Let's imagine we need to add a level of hierarchy in our class. We'll simply put for example the property as virtual.
class UsingAutoImplementedProperties_Evolution2
{
public virtual int MyValue { get; set; }
public UsingAutoImplementedProperties_Evolution2()
{
MyValue = 10;
}
}
And let's create any derived class, let's imagine like this.
class DerivedClass : UsingAutoImplementedProperties_Evolution2
{
private OtherClass objectInitializedInCtor;
public DerivedClass()
{
objectInitializedInCtor = new OtherClass();
}
public override int MyValue
{
get { return base.MyValue; }
set
{
objectInitializedInCtor.DoSomething();
base.MyValue = value;
}
}
}
What is the consequence ? When I will do a new DerivedClass(), here is the workflow that will occur (I will simplify it by giving only the steps related to the code example) :
-
Initializing the variables of the class
-
Calling the constructor of the class
-
Calling the need for building the base class
-
Initializing the variables of the base class
-
Calling the constructor of the class
-
Calling the property MyValue to set 10
-
This will in fine call the property MyValue of the derived class
-
This will call the method DoSomething on the objectInitializedInCtor (which is null)
-
This will crash
This case is pretty simple as it will crash. Let's imagine now we are dealing with a collection that has been created, but not yet filled in, or on some values that still have their default value instead of the real one, this may cause very tricky bugs to find out.
Is that bad ? It's not my code...
What can we see here ? The application may crash / bug not because of the original code, but because of the evolutions we may bring to this code. The question may be "who is responsible for the bug ?".
Well in my view, the responsible is for sure the first code and NOT the evolution. Indeed we have assumed that our code WILL NOT evolve, and so we have put in place un-needed risks. And the guy making the evolution has just made a simple evolution, touching simply what he would need, and not more. I would even say, just what he was supposed to touch.
Imagining that the original code will not take this risk, is that defensive programmation ? Not in my view. For me defensive programmation is putting many things in place, because we thing the next one will add some bad / buggy code. Here it's the contrary, it's just writing the code to avoid having the next developper needing to think the ones before has written some bad / buggy code.
Does this resolve everything ?
No of course. But before detailing any other aspects related to the risky original code, or to the proposed code (ie without using properties in the constructor), I would like to listen to the reactions of all of you. To know what you think on the subject and which limitations you see.
I have given here some clues about creating control adapters. A few days after I have started to use them, I have encountered some very special problems. In fact the adapters have the following hierarchy : the .NET framework expose a ControlAdapter from which derives a WebControlAdapter and a PageAdapter. THe name of these classes being explicit enough to let you know in which cases you should use which one.
As a consequence, when you want to adapt a WebControl, you should use a WebControlAdapter or one of its derived classes. So far, so good, so simple.
But what you should know is that the framework expose in the WebControl class a method called Render. Never seen it or overriden it ? Normal it's a protected internal one. This method is responsible of the rendering of the control and so does : RenderBeginTag, RenderContent and RenderEndTag. And these are the three methods you will normally work with.
And so what does the WebControlAdapter : it will redefine the Render method to do the same work : RenderBeginTag, RenderContent and RenderEndTag. And each of these methods will either call the apdater's equivalent method, or the one of the control itself.
Pretty normal. No problem there.
Well... Almost... Let's imagine just one moment, that someone has defined some other code in the Render method ? Well... That would bring some troubles no ? Right it would break the .NET standard but let's imagine. Of course, in that case, using a WebControlAdapter would just bring a you a no-longer working control.
It's in fact what happens in the BaseValidator class (which inherits from WebControl of course). The Render method does a lot more, like rendering javascript, the "display" and "visibility" attribute.
Which conclusion, what can we say now ? Never use a WebControlAdapter when working with a BaseValidator (or any derived class). Prefer working with a ControlAdapter instead !
If you dynamically add worksheets, you may need to delete them also. How to do that ? Simple. You can write a simple function as :
public static void DeleteWorksheets(IExcel.Sheets worksheets, params string[] excludedNames)
{
for ( int i = worksheets.Count; i > 0; i-- )
{
IExcel.Worksheet sheet = worksheets[i] as IExcel.Worksheet;
if ( !excludedNames.Contains(sheet.Name) )
sheet.Delete();
}
}
How to use it ? Well if you are in a sheet code and that you want to delete all the sheets except the current one, you can do :
ExcelUtils.DeleteWorksheets(Globals.ThisWorkbook.Worksheets, this.Name);
Almost good. You will simply notice that each time Excel wants to delete a sheet, you will get a popup asking to confirm the deletion. How to bypass it ? Let's modify our function as follows :
public static void DeleteWorksheets(IExcel.Sheets worksheets, params string[] excludedNames)
{
bool displayAlerts = worksheets.Application.DisplayAlerts;
try
{
worksheets.Application.DisplayAlerts = false;
for ( int i = worksheets.Count; i > 0; i-- )
{
IExcel.Worksheet sheet = worksheets[i] as IExcel.Worksheet;
if ( !excludedNames.Contains(sheet.Name) )
sheet.Delete();
}
}
finally
{
worksheets.Application.DisplayAlerts = displayAlerts;
}
}
And here we are !
We have seen here in a previous post how to set some values in cell. We'll see now how to format the cells.
The examples shown here are quite simple but as all - or so - properties methods take only objects and that the MSDN gives only sparingly the real type to use.
Note that for all the following examples and to focus on the different types we can use, I have defined two aliases :
using TExcel = Microsoft.Office.Tools.Excel;
using IExcel = Microsoft.Office.Interop.Excel;
How to set some basic properties :
cell.Font.Bold = true;
cell.HorizontalAlignment = IExcel.XlHAlign.xlHAlignCenter;
cell.VerticalAlignment = IExcel.XlVAlign.xlVAlignCenter;
cell.WrapText = true;
How to set the width on a column :
Note that here, the width is given in points.
How to set the formatting display on a cell :
How to find the correct formatting cell ? Well you can see in Excel directly in the cell format dialog editor. In the "custom" tab you will see some examples of the string you can give.
cell.NumberFormat = "0.00";
cell.NumberFormat = "#.##0.00";
cell.NumberFormat = "dd/MM/yyyy";
Second article of the serie. Now I need to control a chart and to create some series.
Note that in my case, I have added a graph (empty) on a page, to be sure of the position and size (even if we can also control that by code). As a consequence, I will control the graph Globals.MySheet.MyGraph and I will store it in a variable chart.
Microsoft.Office.Tools.Excel.Chart chart = Globals.MySheet.MyChart;
How to add the source data of a graph ?
There is many way to add the source data of a graph. The simplest ? to use the SetSourceData method.
chart.SetSourceData(sheet.Range["B1", "B4"], XlRowCol.xlColumns);
chart.SetSourceData(sheet.Range["B1", "C4"], XlRowCol.xlColumns);
Here it's a little bit tricky to understand. In the first case, Excel will construct one serie to the graph. It will also try to extract the title from the data. How ? well it will probably check the type of the data to see, but it's a bit "magic". In the second case, it will construct two different series.
How to manipulate the series ?
The "simplest" and the most powerful way is to work directly with the series. You will note here the use of "System.Type.Missing". Indeed this is an optional parameter. When giving a string or an int, it will return a single Series. To get the wole collection, we have to omit this parameter.
SeriesCollection series = chart.SeriesCollection(System.Type.Missing) as SeriesCollection;
Series serie = series.Add(sheet.Range["B2", "B4"], XlRowCol.xlColumns, false, false, false);
serie.XValues = sheet.Range["A2", "A4"];
serie.Name = "Name of my first serie";
serie = series.Add(sheet.Range["C2", "C4"], XlRowCol.xlColumns, false, false, false);
serie.XValues = sheet.Range["A2", "A4"];
serie.AxisGroup = XlAxisGroup.xlSecondary;
serie.Name = "Name of my second serie";
How to manipulate the axis ?
Excel.Axis axis = (Excel.Axis)chart.Axes(XlAxisType.xlValue, XlAxisGroup.xlPrimary);
axis.HasTitle = true;
axis.AxisTitle.Text = "My left axis title";
axis.AxisTitle.Top = chart.PlotArea.Top;
axis = (Excel.Axis)chart.Axes(XlAxisType.xlValue, XlAxisGroup.xlSecondary);
axis.HasTitle = true;
axis.AxisTitle.Text = "My right axis title";
axis.AxisTitle.Top = chart.PlotArea.Top;