Comments, how-to's, examples, tools, techniques and other material regarding .NET and related technologies.
Thursday, June 04, 2009
WPF and Silverlight: Sharing User Controls
Check out my blog on Agile Leadership.
Friday, May 22, 2009
ReSharper 4.5 Memory Consumption
Just tried ReSharper on a fairly large solution with about 38 projects (13 C# and 25 C++). With the ReSharper addin enabled the memory consumption is sitting at about 1.5 GByte for the solution. With the ReSharper addin disabled the memory consumption is sitting at about 300 MByte for the solution. The solution itself has over 90% of its source code in C++ (mix of managed and unmanaged) and only a smaller portion in C#.
JetBrains claim they have worked on the memory consumption of ReSharper 4.5. Looks to me as if more work is needed ....
On my 32 bit box I have to reboot once in a while because I'm running out of memory when the addin is renabled ... (Vista 32 bit, VS 2008, 4 GB RAM)
Update on 04 June 09: It looks as if the memory consumption goes up to the same value with R# disabled. This happens when IntelliSense creates/updates its database. However, with R# disabled the memory drops back to normal once IntelliSense has finished that activity. With R# enabled it appears as if IntelliSense doesn't free up the memory. So it looks as if the problem is caused by the combination of the two. A solution with just C# project (and hence without IntelliSense in C++) doesn't seem to have that issue either.
Since I am in contact with JetBrains at the moment let's see what they can find based on the info I can provide.
Update on 02 February 2010: I had some conversations with JetBrains and they have asked me to provide an example that show the behavior. The challenge is that is seams to happen (or become apparent) only when there is a large number of C++ projects in the solution and the entire C++ code base is significant as well. While I do have an example to demonstrate the behavior, I’m a bit challenged to provide the very example. Would you send your entire code base? At the moment some members of my team have switched off ReSharper when they are working with the solution that also contains C++ projects.
Also check out my blog on Agile Leadership.
Monday, May 18, 2009
A comment on McAfee
- Wait until McAfee has finished scanning my system.
- Cancel the system scan so I can scan the downloaded file
Friday, May 15, 2009
I couldn't resist... - a quote from Subversion's web site
"all the memory we allocate will be cleaned up eventually" (Source: "Hacker's Guide To Subversion", retrieved 15 May 2009)I like that quote since I think it is true for all software! "Eventually" all memory will be cleaned up, and if necessary when the process terminates. On second thought, though, here might be an opportunity for a new business model! What if we had one-way memory? You can allocate it once, and once a process has consumed it you need to buy new DRAM (or maybe it could be called OW-DRAM as in "One Way"-DRAM). I'm sure Intel and other chips vendors would love it! But seriously (and I'm sure I've got some funny things somewhere in my published text and code as well. Tell me!): A memory leak is a memory leak is a memory leak. Using add and commit for adding large amounts of files consumes about 1 KByte per file. In one case I tried to add about 13,000 files and the process fell over when it reached 1.3 GByte having added only 10% of the files. So this approach doesn't work for version 1.6.1. All indications are that this is a memory leak. It is proportional to the number of files you add and try to commit. OS tools show how the process grows in size. It never shrinks (unless the process terminates one way or the other). Admittedly I didn't use a memory profiler, but what do you think the issue is when an error is reported on the command line saying "out of memory"? The better option for getting large sets of files into a repository - and that's what I learned by now - is to use SVN's import functionality. I have made several tests now with up to 65,000 files in one batch (several hundred MB) and the memory consumption of the client process grew only very slowly - from about 10 MB to 58 MB at most. This growth - I suspect - is probably related to memory fragmentation but definitely within acceptable limits. So the recommendation is: Don't use svn add and then svn commit. Instead use svn import if you have large sets of files to import. If they go into the wrong place you can always move them later using a repository browser such as the one that comes as part of TortoiseSVN.
Saturday, May 09, 2009
Handling of middle mouse click in Firefox 3.0
Friday, May 08, 2009
SVN Client: Out Of Memory
Sunday, April 26, 2009
ListView.GetItemAt() in WPF Application
private void _replacementsListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
var mousePosition = e.GetPosition(_replacementsListView);
var item = _replacementsListView.GetItemAt(mousePosition);
if (item != null
&& item.Content != null) {
EditTokenReplacement((TokenReplacement) item.Content);
}
}
Since the WPF class ListView doesn't come with a built-in method I decided to implement an extension method as follows:using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace Foo.Gui { internal static class Extensions { public static ListViewItem GetItemAt(this ListView listView, Point clientRelativePosition) { var hitTestResult = VisualTreeHelper.HitTest(listView, clientRelativePosition); var selectedItem = hitTestResult.VisualHit; while (selectedItem != null) { if (selectedItem is ListViewItem) { break; } selectedItem = VisualTreeHelper.GetParent(selectedItem); } return selectedItem != null ? ((ListViewItem) selectedItem) : null; } } }Please note that the position is passed into the method as an object of class Point. This means that the coordinates need to be relative to the list view object. In an event handler for a mouse button event, e.g. a double click, these can be calculated by using a method on MouseButtonEventArgs object that is passed into the mouse event handler. It's already shown in the first code snippet above but just to be on the safe side here are the relevant lines again:
private void _replacementsListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
var mousePosition = e.GetPosition(_replacementsListView); // gets position relative to ListView
var item = _replacementsListView.GetItemAt(mousePosition);
...
}
Saturday, April 25, 2009
Getting Parent Window for UserControl in WPF
public partial class FooControl : UserControl { public FooControl() { InitializeComponent(); } private void _barButton_Click(object sender, RoutedEventArgs e) { var window = new MyWindow { WindowStartupLocation = WindowStartupLocation.CenterOwner, Owner = Window.GetWindow(this) }; if( window.ShowDialog() == true) { // Ok button clicked, do something useful here... } } }
Sunday, April 19, 2009
OpenFileDialog in .NET on Vista
In addition even when I passed the owner as a parameter to ShowDialog() it wouldn't center relative to that owner window.
Then I tried the previous System.Windows.Forms.OpenFileDialog(). Although it is not recommended to use that namespace within WPF I thought it would still be worth a try. And voila! Here is the result under Vista:
Not only did it display the correct dialogbox under Vista but in addition it's location centered on it's owner is correct as well.
To achieve this you have to fiddle a bit with the namespaces as there are a few classes that have the same name in both Microsoft.Win32 and in System.Windows.Forms. Since I wanted the client code to look as simple as possible I created two very small wrapper classes.
The first wrapper class is basically a replication of the Microsoft.Win32.OpenFileDialog interface (I left out most of the code since it is simply forwarding):
using SWF = System.Windows.Forms; namespace FooApp.Presentation { internal class OpenFileDialog { public OpenFileDialog() { _dialog = new SWF.OpenFileDialog(); } public bool? ShowDialog(System.Windows.Window window) { return _dialog.ShowDialog(new WindowWrapper(window)) == SWF.DialogResult.OK; } public string DefaultExt { get { return _dialog.DefaultExt; } set { _dialog.DefaultExt = value; } } public string FileName { get { return _dialog.FileName; } set { _dialog.FileName = value; } } public string Filter { get { return _dialog.Filter; } set { _dialog.Filter = value; } } public string Title { get { return _dialog.Title; } set { _dialog.Title = value; } } private readonly SWF.OpenFileDialog _dialog; } }The only really interesting piece is the ShowDialog() method which takes a WPF window. System.Windows.Forms.DialogBox.ShowDialog() requires a IWin32Window instead (for easy access to the window handle), so we need a second wrapper class that looks as follows:
using System; using System.Windows; using System.Windows.Interop; namespace FooApp.Presentation { /// <summary>WindowWrapper is an IWin32Window wrapper around a WPF window. /// </summary> /// <remarks>Add System.Windows.Forms to the references of your project to /// use this class in a WPF application. You don't need this class in a /// Forms based application.</remarks> internal class WindowWrapper : System.Windows.Forms.IWin32Window { /// <summary> /// Construct a new wrapper taking a WPF window. /// </summary> /// <param name="window">The WPF window to wrap.</param> public WindowWrapper(Window window) { _hWnd = new WindowInteropHelper(window).Handle; } /// <summary>Gets the handle to the window represented by the implementer. /// </summary> /// <returns>A handle to the window represented by the implementer. /// </returns> public IntPtr Handle { get { return _hWnd; } } private readonly IntPtr _hWnd; } }With this machinery in place the client code in a WPF based application looks very familiar and simple:
var dialog = new OpenFileDialog { DefaultExt = ".dll", Filter = "Assemblies (*.dll; *.exe)|*.dll;*.exe", Title = "Select Assembly" }; if( dialog.ShowDialog(this) == true ) { // File is selected, do something useful... }As a result you have it all, simple client code, the correct OpenFileDialog on Vista and the dialog centered properly on its owner. The only thing that I still can't get my head around: After 8 years of .NET we still don't have a class available that is based on Window (or Form) and can be customized. It seems as if this is a puzzle that the guys in Redmond haven't figured out yet...
Wednesday, April 15, 2009
Separator for Menu Item in XAML (WPF, Silverlight)
<Menu ...
<MenuItem ...
<MenuItem ...
<Separator />
<MenuItem>
</MenuItem>
</Menu>In C# use:
using System.Windows.Controls; Menu menu = new Menu(); ... menu.Items.Add(new Separator());
Tuesday, April 14, 2009
LinearGradientBrush with more than two GradientStop's in XAML
Using Expression Blend 2 you may believe that you can have only two GradientStop's for your LinearGradientBrush in particular if you are UI addicted as I am. The UI of Expression Blend 2 doesn't offer adding another GradientStop.
But funny enough it supports them! So just go to the XAML code for, e.g. select "View XAML" from the context menu and just add another GradientStop tag to the XAML code for the GradientBrush.
Once you do that you will notice that the Expression Blend 2 user interface will happily display 3 (or more) sliders as demonstrated in the screenshot.
Here is an example:
<lineargradientbrush key="WindowBackgroundBrush"
spreadmethod="Pad" endpoint="0.5,1" startpoint="0.5,0">
<gradientstop color="#FFFFDCDC" offset="0.56" />
<gradientstop color="#FFFF6F6F" offset="0.77" />
<gradientstop color="#FFFFDCDC" offset="1" />
</lineargradientbrush>
Monday, April 13, 2009
TextBox and other Controls with Transparent Background in XAML
- Set its Background property to 'Transparent'
- In Expression Blend select the Background brush and set the alpha channel to zero
Sunday, April 12, 2009
"The file 'x' is not part of the project or its 'Build Action' property is not set to 'Resource'"
The file 'x' is not part of the project or its 'Build Action' property is not set to 'Resource'It's not quite clear to me what causes this and it's not quite clear why Microsoft didn't fix it in Service Pack 1 for Visual Studio 2008 but here is a solution that may work for you:
- Add the file to your solution.
- Set its 'Build Action' to 'Resource' (in my case the drop-down also offers 'Embedded Resource' but that's not what you want)
- Select the image control.
- Set its Source property to the image file name. It should show up in the drop-down list. Next it may display the error message mentioned above. Then rename the image file to a short filename. Try setting the Source property of the Image control again. It should be fine now.
<Image ... source="Foo.Gui;component/pictures/myLogo.jpg" ... />The assumptions for this example are
- Your project is named 'Foo.Gui'
- That project contains a folder named pictures
- Your picture is in that folder and also included in the project. The name of the picture is 'myLogo.jpg'
Saturday, April 04, 2009
"Configuration system failed to initialize"
Friday, April 03, 2009
Membership Provider Implementation using NHibernate
Saturday, March 28, 2009
csUnit 2.6 Released
- csUnit is now based on .NET 3.5 SP 1
- Parameterized testing moved out of experimental
- Basic support for Microsoft unit testing.
- Several bug fixes.
- csUnit (no surprise!)
- NUnit 2.4.7 (.NET 2.0)
- Microsoft Unit Testing (basic support)
Friday, March 20, 2009
"Failed to create a service in configuration file"
- In the web.config file locate the XML node
within the node . For instance in the above example locate the node that describes the 'BarService'. Delete that node but leave in the file. - In the same file locate the node
with an attribute 'behaviorConfiguration' and an attribute value of 'BarService'. This node also contains the endpoints for the service. Remove this node. - Try again, and this time it should work.
Thursday, March 19, 2009
Updating Silverlight User Interface From Timer
// _statusMessage is a System.Windows.Controls.TextBlock object
// _statusMessageTime is a System.Threading.Timer object
private void DisplayStatusMessage(string message) {
_statusMessage.Text = message;
_statusMessageTimer = new Timer(ResetStatusMessageCallback,
/* params left out for brevity */);
}
private void ResetStatusMessageCallback(object stateInfo) {
_statusMessage.Text = "";
}When the timer fires the callback is executed on a thread different to the user interface thread. The UI will not update. In my installation the Silverlight interface would then simply disappear! To fix this you need a way to execute the actual update on the user interface thread. One way is to use the Dispatcher object (Namespace: System.Windows.Threading) of the XAML page. Then the code of the callback implementation looks as follows: private void ResetStatusMessageCallback(object stateInfo) {
Dispatcher.BeginInvoke(() => {
_statusMessage.Text = "";
});
}Another solution would be to use the DispatcherTimer. I spare you the details but you can check here for an example.
Monday, March 16, 2009
ClassInitialize Method Can Be 'static'
- TestClassAttribute
- TestMethodAttribute
- ExpectedExceptionAttribute
- ClassInitializeAttribute
Sunday, March 15, 2009
Implicit Typing Can Make TDD Harder
public class Result { public long Status { get; set; } } public class Foo { }Given this starting point you may want to write the following unit test to ensure that class Foo requires a method 'Result NewMethod()':
[TestMethod] public void TestNewMethod() { var foo = new Foo(); var result = foo.NewMethod(); Assert.AreEqual(0, result.Status); }As you type you will notice that when you have typed the dot after 'result' you will not be offered the member list of Result. It's not possible to implicitly type the variable 'result' since the method 'NewMethod()' doesn't exist yet. As a result writing tests in a TDD approach is slowed down when using 'var' instead of explicit types. Here is another view you may take: Writing tests for 'NewMethod()' should include all specifications, including the type of the return value. If you agree with that view you may want to avoid using 'var' in your unit tests. This certainly doesn't apply to people who create their unit tests after the method has been added to the class. I personally wouldn't call this test-first development, let alone test-driven development (or test-driven design as some people argue). Bottom line it depends on where you are coming from. 'var' might not always be the best choice even if it is a new 'cool' feature in C# 3.0.
Saturday, March 14, 2009
Passing Serialized Exceptions as Service Faults
using System.Runtime.Serialization;
namespace AgileTraxx.Services {
[DataContract]
public class ServiceResult {
public ServiceResult(string message, long incidentId) {
Message = message;
IncidentId = incidentId;
}
[DataMember]
public string Message { get; set; }
[DataMember]
public long IncidentId { get; set; }
}
}This class would just take the incident id plus a message. The message could contain information about how to contact support and to note down the incident id.
As you can see there is a lot to consider when designing a service interface, including security related factors.
Sunday, March 08, 2009
Service Reference for method returning void and out parameter
Thursday, March 05, 2009
Domain Objects with Validation in Fluent NHibernate
internal class WorkItem {
public WorkItem () {
}
public virtual long Id {
get {
return _id;
}
set {
_id = value;
}
}
public virtual string Title {
get {
return _title;
}
set {
_title = Validation.EnsureNonEmptyString(value, "Title");
}
}
private long _id;
private string _title = "";
}
I left most of it out. For now let's look at just the id and the title since those two demonstrate sufficiently the issue.
What you will notice is that the setter for the title contains validation code ("Validation.EnsureNonEmptyString(...)").
The problem starts when you query for one or more WorkItem's. Then NHibernate will use the property setters to initialize the instance of WorkItem. For strings the default value is null (nothing in VB.NET). With the given code, however, the validation will throw an exception since that is what it is designed to do. It doesn't care whether the setter is called by NHibernate or anything else.
So next I tried to figure out what alternatives I would have for validation and I found NHibernate.Validator. Although a step in the right direction I didn't like that the client code for the domain objects would have to explicitly call the validation. Alternatively the validation would have to be invoke via the event call backs from NHibernate. In both cases the domain class would only work properly if something else would collaborate. I didn't like that concept and started to look for an alternative.
And there is a quite simple solution to this: Change the configuration for NHibernate so that it doesn't use the properties to initialize the domain objects. This configuration change can be done via Fluent NHibernate as follows:
_hibernateConfig = new Configuration();
AutoPersistenceModel persistenceModel =
AutoPersistenceModel
.MapEntitiesFromAssemblyOf()
.Where(TypeIsIncluded)
.ForTypesThatDeriveFrom(
map => map
.DefaultAccess
.AsCamelCaseField(Prefix.Underscore));
persistenceModel.Configure(_hibernateConfig);
Depending on your naming conventions you may want to use a different access strategy or a different Prefix value. In my case it was Camel Casing with an underscore as a prefix.
After I finally found this solution I was able to keep the domain classes squeaky clean and at the same time stay with using the Fluent NHibernate interface and avoiding exception during the initialization of instances of domain classes.
Of course I'm not sure whether this is the only and/or best option. If you have other options that are even better, please drop me a note. I'm always keen to learn more!
Tuesday, March 03, 2009
Executing MS Unit Tests in csUnit
- TestClass
- TestMethod
- ExpectedException
Monday, March 02, 2009
Another Tool for Silverlight Unit Testing
Sunday, March 01, 2009
csUnit migrated to .NET 3.5 and VS 2008
Saturday, February 28, 2009
Designing Service Interfaces For Silverlight Clients
Saturday, February 14, 2009
Parser Error Message: Could not load type 'Global'
- Open the file containing your class Global typically located in the file Global.asax.cs,
- Note the namespace for that file
- Open the file Global.asax
- Locate the line that contains the element "... Application Codebehind=..."
- In that line ensure that the element "Inherits=..." includes the namespace for your Global class, e.g. MyWebSite.Global, (Inherits="MyWebSite.Global"). Replace MyWebSite with the namespace noted in step 2
- Recompile and redeploys.
- The error should be gone.
Tuesday, January 13, 2009
Unit Testing for Silverlight
Tuesday, June 24, 2008
Watch Out: Window.ObjectKind is UPPER CASE!
string guidpos = "{426E8D27-3D33-4fc8-B3E9-9883AADC679F}";
string guidpos = "{426E8D27-3D33-4FC8-B3E9-9883AADC679F}";
=====
I've highlighted the important bit with red color, bold, and a larger font. In addition I have underlined it. I think you got the idea.
Why is this change important? Assume you iterate over the collection as follows:
foreach(Window2 toolWin in toolWins) {
string toolWinKind = toolWin.ObjectKind;
if( toolWinKind.Equals(guidString) ) {
_toolWindow = toolWin;
break;
}
}The Equals() call may always result in 'false' even if you think you are looking at the correct tool window. The reason is that ObjectKind returns the guid all upper case. The example code has two lower case characters in the guidpos variable.
The same can also happen when you use "Create GUID" from the "Tools" menu in Visual Studio. It may generate a guid for you with one or more lower case digits ('a' through 'f') as well.
It's unfortunate that the online documentation doesn't mention that some time between calling CreateToolWindow2() and calling ObjectKind everything is made upper case. The example code leads to potentially incorrect behavior of your addin as well. This post gives you the heads-up.
It took me quite some time to spot this little difference. In my case it was 'f' versus 'F' and only when Equals() insisted on returning false I took a closer look. Maybe this helps you saving some development time.
And maybe someone from Microsoft is reading this. I tried to add it as Community Content at MSDN. It wasn't possible. So I rated the article and left a comment adding the suggestions for improvement. This would have been ideal for "Community Content". MSFT, you mind updating the online material? Thank you!
Thursday, June 19, 2008
csUnit: What's Next?
Saturday, June 14, 2008
Vista: Finding Encrypted Files
- Open a command prompt and switch to a directory in which you have write permissions.
- Run the command: "cypher /s:c:\ /N /U > filelist.txt" and wait until finished (this example searches the entire volume c:
- When finished open the file filelist.txt. It contains a list of all files that are encrypted.
- In the Explorer window navigate to each file and though "Properties" -> "Advanced..." goto to the "Advanced Attributes" page and remove the checkmark from "Encrypt contents to secure data"
Tuesday, June 10, 2008
Addin Command Names
Saturday, May 31, 2008
Custom Installer Creating Folder, Setting Permissions
The first thing to know is that an installer typically runs with elevated rights as the discussion on Chris Jackson's blog indicates. This means that your installer has sufficient rights to create directories but also to set appropriate permissions.
And here is the code for creating a directory and setting permissions:
public static void CreateWithReadAccess(string targetDirectory) {
try {
if(!Directory.Exists(targetDirectory)) {
Directory.CreateDirectory(targetDirectory);
}
DirectoryInfo info = new DirectoryInfo(targetDirectory);
SecurityIdentifier allUsersSid =
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid,
null);
DirectorySecurity security = info.GetAccessControl();
security.AddAccessRule(
new FileSystemAccessRule(allUsersSid,
FileSystemRights.Read,
AccessControlType.Allow));
info.SetAccessControl(security);
}
catch(Exception ex) {
Debug.WriteLine(ex.ToString());
}
}This code checks for existence of the directory first. If the directory doesn't exist yet it is created. Then the security settings are applied. In this case the Read permissions are granted to all members of the group BUILTIN\Users.
By selecting another member of the WellKnownSidType enumeration you can grant permissions to a different group. Alternatively, if you'd like to grant permissions to a specific user, have a look at the NTAccount class. An instance of it can be passed into the FileSystemAccessRule constructor as a first parameter as well.
Tuesday, May 27, 2008
ReSharper 3.1 and Empty Constructor
#pragma warning disable EmptyConstructorThe compiler now complains about 'EmptyConstrutor' not being a valid number. Fair enough it is not.
So I removed the pragma since any other pragma would get ReSharper complaining again. To keep ReSharper quiet the constructor now looks as follows:
public class Foo() {
// ... the XML markup for documentation
public Foo() {
; // to shut up ReSharper
}
}Now both, the compiler and ReSharper, are happy! :-)
No big issue but the better solution would have been if ReSharper would modify the code only in such a way that it doesn't cause additional warnings.
Update
This applies to essentially all such pragma's ReSharper inserts, including but not limited to:
- UnusedMemberInPrivateClass
- PossibleNullReferenceException
As stated before: Best option would be if the code inserted by ReSharper would not cause additional warnings from the C# compiler. Or in other words: The added code should be "compatible" with the compiler. One option could be to use comments instead. The compiler ignores them.
The C# preprocessor is not as powerful and flexible as the C/C++ one. The latter allows your extensions by ignoring unknown ones. The C# version does not have that "back door" but checks them for validity as well. In essence you can't define your own. (Or at least you shouldn't if you don't want to cause compiler warnings.)
Thursday, May 15, 2008
Outlining/collapsing Comments in VS2005/VS2008
Tuesday, May 13, 2008
SerializationException - "The constructor to deserialize an object of type 'foo' was not found."
- Add the SerializableAttribute to the class.
- Have the class implement the ISerializable interface.
- Request sufficient privileges for your GetObjectData() implementation.
In essence you are rolling custom serialization. And here is how Foo would look like with all these in place:
using System.Runtime.Serialization;
using System.Security.Permissions;
...
[Serializable]
public class Foo : ISerializable {
protected Foo(SerializationInfo info,
StreamingContext context) {
_barString = info.GetString("_barString");
_barInteger = info.GetInt32("_barInteger");
}
[SecurityPermissionAttribute(
SecurityAction.Demand,
SerializationFormatter = true)]
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context) {
info.AddValue("_barString", _barString);
info.AddValue("_barInteger", _barInteger);
}
private string _barString;
private int _barInteger;
}Note that the constructor is protected. This way it cannot be called except from the serialization code within .NET. It is however accessible by code in subclasses.
If your class is derived from a class that implements ISerializable, then you need to call the base class in both the constructor and the GetObjectData() implementation as follows:
[Serializable]
public class Foo : Base { // Base implements ISerializable
protected Foo(SerializationInfo info,
StreamingContext context)
: base(info, context) {
// your deserialization code
}
[SecurityPermissionAttribute(
SecurityAction.Demand,
SerializationFormatter = true)]
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context) {
base.GetObjectData(info, context);
// your serialization code
}
}For more information please check MSFT's web site here.
Sunday, May 11, 2008
Weakness in VS Debugger
public class ProjectElement {
...
public override bool Equals(object obj) {
if( obj != null
&& obj.GetType().Equals(GetType())) {
ProjectElement otherObject =
(ProjecElement)obj;
return _assemblyPathName.Equals(
otherObject._assemblyPathName);
}
}
...
}
During a debugging session you can use the QuickWatch as a convenient way to examine variables.
However, be aware of the following. When you use QuickWatch on '_assemblyPathName' in the above example then QuickWatch does not consider the context. E.g. even if you hover over the '_assemblyPathName' part of the expression 'otherObject._assemblyPathName' QuickWatch will just use the member variable of 'this', which not in all cases will have the same value.
If you want to be on the safe side then select the entire expression you would like to inspect, e.g. all of 'otherObject._assemblyPathName'. After that select QuickWatch.
I think this is a shortcoming of the QuickWatch feature since it displays the content of a variable that you didn't want to look at, and you might not even notice that you are looking at something different of a similar name.
If one of you Microsofties are reading this maybe you could consider this as an improvement for the next release of Visual Studio? Thank you!
Thursday, May 08, 2008
Implementing Properties: Basic Considerations
public class Foo { private string _bar; }Now we want to add an accessor aka the getter:
public class Foo { public string Bar { get { return _bar; } private string _bar; }This has been the easy part. There is not really a lot that can go wrong.
It starts to become more interesting when you add a modifier aka a setter. In it's most simple form you could write:
public class Foo { public string Bar { get { return _bar; } set { _bar = value; } private string _bar; }Easy you think. But hang on. There is more to it. You may want to decide whether it is acceptable to pass in a null reference or not. Whether you allow for null or not is a decision that should be made based on a number of factors. One way to find out is to look at all the places in your code where the getter is used. What would happen to that code if null would be returned? For example:
Foo foo = new Foo(); ... if( foo.Bar.Length > 25 ) { ... } ...In that case if null was returned this piece of code would crash. You could fix this issue by checking for nullness:
Foo foo = new Foo(); ... if( foo.Bar != null && foo.Bar.Length > 25 ) { ... } ...This certainly work but you pay the price of a slightly less readable code. In addition you may have to have this in a lot of places. So in essence you may decide that the property Foo.Bar doesn't allow for null values. The code for class Foo would then look as this:
public class Foo { public string Bar { get { return _bar; } set { if( value != null ) { _bar = value; } } private string _bar; }This clearly provides the benefit of Foo.Bar never being null since upon initialization _bar will be initialized with string.Empty or "".
But again this comes at a price. The setter simply swallows the attempt to set Foo.Bar to null. This might be desirable. I personally prefer that a class doesn't swallow incorrect things but instead fails fast. In this particular case I would want my code to indicated the error by throwing an exception:
public class Foo { public string Bar { get { return _bar; } set { if( value != null ) { _bar = value; } else { throw new ArgumentNullException("value"); } } private string _bar; }You see that although this is a simple property implementation it can already require quite a few decisions to be made and aspects to be considered.
To close off this particular post, I'd like to also bring performance considerations into the picture. What if the setter needs to validate any new value against a remote system such as a service? Let's look at the possible code:
public class Foo { public string Bar { get { return _bar; } set { if( value != null ) { if( _validationService.IsPermitted(value) ) { _bar = value; } else { throw new ArgumentOutOfRangeException("value"); } } else { throw new ArgumentNullException("value"); } } private string _bar; private ValidationService _validationService = new ValidationService(...); }Calling IsPermitted() can be quite expensive. So how to avoid this? Here is one possible solution:
public class Foo { public string Bar { get { return _bar; } set { if( _bar != value ) { if( value != null ) { if( _validationService.IsPermitted(value) ) { _bar = value; } else { throw new ArgumentOutOfRangeException("value"); } } else { throw new ArgumentNullException("value"); } } } private string _bar; private ValidationService _validationService = new ValidationService(...); }With this implementation the validation service is called only if the value has actually changed. Certainly if the set of permitted values is dynamic this implementation would not make the cut. With this post I want to demonstrate that even property that looks like an easy thing to do already requires a lot of considerations. We even touched performance briefly. It is important that we are aware of all of these aspects when implementing and testing such a property. There are more aspects to this but I think I've made my point. Even with simple things like properties there are already a quite a few aspects to consider.
Sunday, April 27, 2008
Codeplex, IE, and Firefox
Saturday, April 26, 2008
Accessing Installation Properties in Custom Actions
/TargetDir="[TARGETDIR]\"Please note the trailing backslash, which you only need to use when you surround the property in double quotes. However, as a safety measure I suggest making it a habit to always add the double quotes and the backslash. Remember paths can and will contain spaces! In some examples on the internet the backslash may be mentiond in the text but not be included in the code sample. If you leave the backslash out you'll see a message box containing "error code 2869". However, this article makes it clear that you must add the trailing backslash if you use the double quotes. Also, if you want to pass more than one property separate them by a single space, e.g.
/TargetDir="[TARGETDIR]\" /UserDir="[PersonalFolder]\"Then in your custom action implementation - a class derived from System.Configuration.Install.Installer - you can access it in the following way (C# here, but similar in other .NET languages):
string targetDir = Context.Parameters["TargetDir"];That's all.
Two More Tips for Debugging Custom Actions
Visual Studio: Access denied when attaching debugger to process
Thursday, April 17, 2008
MySQL closed-source: What's the alternative?
Wednesday, April 16, 2008
ReSharper 3.1: More Details
Tuesday, April 15, 2008
What's next for csUnit?
ReSharper 3.1 in Visual Studio
Tuesday, April 08, 2008
ControlPaint and Vista
Wednesday, April 02, 2008
Property Getters with Bad Manors
public static int Counter {
get {
int cnt = counter;
counter = 0;
return cnt;
}
}Ok, the documentation says that calling this getter will reset the counter. However...
Still, in my opinion this is bad coding practice. Getters shouldn't modify the object. The reason for that is that some IDE's use the getters for displaying object data in debugging sessions. As a consequence debugging code and looking at the member of this object influences the outcome of the debugging session. I call this bad manors of that getter.
The better approach would have been to use a method with a more intuitive name for example "ReadAndResetCounter()". That would a) avoid accidental modification during debugging sessions, b) follow the good coding practice of getters being non-modifying accessors, and c) would express the actual functionality of the code in a more understandable way.
Some recommendations as a take-away:
- Don't implement getters that modify the object
- Instead use a method with a descriptive name
That way you will make it easier for other engineers to use your code and/or library.
(Now I'm wondering how long it will take until Charlie reads this. I'm sure he will agree with me!)
Monday, March 31, 2008
"Location is not available" on Vista
- "Preparing your desktop..." for an extended period of time
- "Location is not available" after you log in
- Desktop looks more like XP and has lost the Aero look-and-feel
I don't know what the reason for this was. Maybe it was relating to my Vodem to not being able to survive stand-by and/or hibernate. Maybe it was because Vista decided to run some lengthy checks, like CHKDSK. The event logs don't give a clue.
Resolution: Reboot another time.
Monday, March 24, 2008
csUnit 2.3 available
unresolved external symbol ?.cctor@@$$FYMXXZ
Monday, March 17, 2008
csUnit, Visual Studio 2008, Vista, .NET 3.x
Thursday, March 13, 2008
csUnit Sources About To Be Moved to Subversion
Visual Studio: "Unable to find manifest signing certificate in the certificate store"
"Unable to find manifest signing certificate in the certificate store"As I was sure that I wasn't using any certificate to sign the assembly I couldn't understand the reason for this error message and the integrated help system for Visual Studio wasn't a big help either. It turned out that I had to manually go into the *.csproj file and remove the following three lines that were apparently left over from some past experiments with signing using a certificate:
<manifestcertificatethumbprint>...</manifestcertificatethumbprint> <manifestkeyfile>...</manifestkeyfile> <generatemanifests>...</generatemanifests> <signmanifests>...</signmanifests>After I had removed those lines I reloaded the project and the solution rebuilt just fine. There is more information on this subject at a Microsoft Forum.