Blog Home  Home Feed your aggregator (RSS 2.0)  
What did you learn today? - DotNet
Phil Denoncourt's Technology Rants
 
 Tuesday, January 06, 2009

The 1st New Hampshire Code Camp is being help Saturday, Feb 28th at Daniel Webster College.  Registration is now open at http://www.thedevcommunity.org/.  We're looking for developers or DBAs to present at this event.  We're also looking for component vendors or recruitment firms to sponsor the event with giveaways, or to help cover food costs. (Contact me - phil@denoncourtassociates.com or Pat Tormey)

I love going to code camps, I always learn a lot about technologies that I don't get to use in my day-to-day life.  It's nice to network with other developers, learn what problems they're running into and how they're solving them.  The manifesto describes it best: By and For the Developer Community, Always Free, No Fluff...

With the economy in apparent decline, a lot of you developers are looking for ways to make yourselves more marketable.  According to ToastMasters International, presentation skills are "crucial to success in the workplace".  Code Camps are a great way to start refining your speaking skills.  Nobody expects a polished speaker.  All we're looking for is someone to show us some code, help facilitate a conversation, or show us something new.  If you get nervous when you speak in front of people and that's deterring you, partner up with a buddy to deliver the talk with you. Presentations should go about an hour, and budget time for dialog/Q&A.

Here's how to register to speak at the NH Code Camp:
1) Create an account at thedevcommunity.org, or sign into your existing one.
2) Complete a Speaker Registry profile
3) On thedevcommunity.org 's home page, click the submit a presentation link next to Code Camp New Hampshire
4) Repeat (as necessary)

Looking forward to seeing you all there...

Tuesday, January 06, 2009 1:35:57 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   Development | DotNet | Speaking Engagements  | 
 Wednesday, October 08, 2008

There really is a single instance of a static field per appdomain.  It kind of says this in the C# language reference, but not very explicitly.  This knocked me around a little because I had assumed that there was a single static field per type within an appdomain.  I had a base type that had a dictionary as a static field and assumed that each class that derived from it had its own instance of the static field.  Not so…

Look at the following test code:

using System;
using System.Collections.Generic;
using System.Text; 

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            baseClass.AStaticValue = 3;
            inheritedClass.AStaticValue = 4;
            Console.WriteLine("baseClass {0}", baseClass.AStaticValue);
            Console.WriteLine("inheritedClass {0}", inheritedClass.AStaticValue);
            Console.ReadLine();
        }
    }
 
    public class baseClass
    {
        public static int AStaticValue = 0;
    }


   
public class inheritedClass : baseClass
    {

    }
  }
 }

I expected the results to be
baseClass 3
inheritedClass 4

Instead, the output is
baseClass 4
inheritedClass 4

So unless you decorate the field with ThreadStatic or ContextStatic attributes, static means just one in an appdomain.  Remember that when you are inheriting from objects that have static fields, there’s only one instance of that static field.

Wednesday, October 08, 2008 8:08:08 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   Development | DotNet  | 
 Wednesday, July 23, 2008

Part of the .NET 3.0 release was the System.Speech namespace.  This gives you easy access to powerful speech capabilities.  Text to Speech, Speech Recognition, and Dictation are all part of this library.  I've tried speech recognition every 5 years or so, and it has always worked, but it never allowed me to work faster than just using my keyboard.

As a consultant, I work on a variety of different machines.  Sometimes just my laptop, sometimes my laptop with an external monitor, and other times a dual monitor system.  Pictured below is how I set it up on my laptop. 

Most panels are auto hide so that I can maximize the amount of space to view the code.  Getting to the solution explorer means I have to take my hands off the keyboard, and go to the mouse to select the tab.  There are keyboard shortcuts, but I either don't remember all of them, or they don't work in all types of documents.

Since speech recognition is so easy to use with the system.speech dll, and Visual Studio addins aren't rocket science either, I built an addin that enables speech recognition within Visual Studio.  Basically, the addin maps a list of words, that when recognized, execute a command in the Command Window.  So, when I need to quickly look at the task list, I don't have to remember the keyboard shortcut, or mouse to it.  I just speak "Task List".  When I need the toolbox, I say, "Toolbox".  I didn't get into dictation, because I don't think it is efficient to speak "for space open parenthesis int i space equal space zero semicolon i less than items period count semicolon i plus plus close parenthesis open curly brace..."

I uploaded the source and an installer here on CodePlex.  Because (I think) Visual Studio 2005 only works with addins written in .NET 2.0, this only works for VS 2008.  Try it out and let me know how it works.

 

Wednesday, July 23, 2008 7:13:18 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   DotNet  | 
 Tuesday, June 10, 2008

Me either.  See here in the C# Language Reference.  This means you can overload the operators for your classes.  The example on MSDN talks about classes that have a true, false, or null (neither true nor false) state.  You could also create a type that can be both true and false.

Overloading operators can lead to code that is difficult to read.  You should only overload operators when it makes sense to do it.  If it is unclear what “if (myType)” means, don’t overload the operators.

I wrote a small rules engine for a company a while back and this would have been helpful.  Supplemental rules were implemented as separate classes so that they could be dynamically strung together in different orders as the business logic changed.

Here’s a contrived sample using the true/false operators:

    public class Customer
    {
        public string CustomerName { get; set; }
        public string EmailAddress { get; set; }
        public decimal OutstandingBalance { get; set; }
    }

    public class CustomerIsGoodRule
    {
        
        public CustomerIsGoodRule(Customer customer)
        {
            this.Customer = customer;
        }

        Customer Customer { get; set; }

        public static bool operator true(CustomerIsGoodRule theCustomer)
        {
            return (theCustomer.Customer.OutstandingBalance <= 0);
        }

        public static bool operator false(CustomerIsGoodRule theCustomer)
        {
            return (theCustomer.Customer.OutstandingBalance > 0);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Customer c = new Customer();
            c.CustomerName = "Joanne Doe";
            c.EmailAddress = "joannedoe@mailinator.com";
            c.OutstandingBalance = 25;

            CustomerIsGoodRule cigr = new CustomerIsGoodRule(c);

            if (cigr)
            {
                SendNewCatalog();
            }
            else
            {
                SendNewStatement();
            }
        }
    }

One thing to keep in mind is that if you provide a definition for true, you must also provide one for false.  Also notice that the logical negation operator (!) was not overriden, so a statement like if (!cigr) fails to compile.

Tuesday, June 10, 2008 12:04:08 PM (GMT Standard Time, UTC+00:00)  #    Comments [2]   Development | DotNet  | 
 Thursday, October 27, 2005
Last night I posted a webcontrol that I wrote to my website that makes life more difficult for spammers. It's a substitute for hyperlink control in ASP.NET named the obscure hyperlink control

One of the ways that spammers get email addresses is that they have programs that spider the web, looking for email addresses embedded in webpages. They target forum based sites because people are more likely to leave their email addresses there. This has caused people to start leaving their email addresses in cryptic formats (for example: me {at} mydomain.com). I find these techniques annoying as an end user trying to contact someone, but I also have to believe that spammers have caught on and look for variants with the word "at" in them. The obscure hyperlink control can be used for any hyperlink, mailto or http. Besides thwarting spammers, another use of the control would be to link to an objectionable site without contributing to its search engine rank.

What the obscure hyperlink control does is scrambles (note - I'm not saying encrypt) the hyperlink when the page is being created on the webserver using a random technique. An scrambled example of my email address is 'mcstiostucoe@ipolamit:hldnnorascae.o'. You can see a functioning example here. A matching javascript function is added to the webpage that unscrambles the hyperlink when the user clicks on it. When you view the source of the webpage, the link is removed, and an onClick handler is added to the hyperlink. Nowhere will you see the text of the hyperlink. It is present in the onClick handler, but it is not very legible. The Url is not stored in Viewstate, so it can't be taken from there, either.

Here are pros & cons of this control:
Pros:
  • Easy to use (works exactly the same as the existing hyperlink control)
  • The hyperlink information is not in the href attribute, but in the onClick (an area that spammers don't always pay atttention to)
  • The diversity of scrambling algorithms makes it difficult for spammers to target a specific implementation
  • Doesn't require a lot of server resources
Cons:
  • Doesn't completely prevent spammers from getting email addresses. A determined spammer could reverse engineer the control. This is just adds a roadblock for spammers.
  • Requires that the user's browser supports javascript and that it is enabled.
  • Limited number of scrambling algorithms. Right now there are 5. If this fills a need, I intend to add more, but it will still be a finite number.
Thursday, October 27, 2005 4:02:31 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   Development | DotNet | ASP.NET  | 
 Wednesday, October 19, 2005

Visual Studio 2003 stores Reference paths for projects in the .User file. Not in the csproj file. I can't see why they wanted to store that information in a separate file. The settings pertain only to the specific project. Anyway, if your application's build is dependant upon having the reference paths set, make sure you are adding the .User files to your source control system. The csproj file is not enough.

Normally you can find the .User file in the same directory as the project file... Unless it is a web application/service. Then you will find the file in the VSWebCache folder. Which makes it very difficult to put that file under source control. I don't see a good solution to this problem. Maybe porting the build to use NAnt. Although that would be burdensome if we just wanted to debug the project. 

I guess the best solution is to architect your project so that it isn't dependant on a reference path.

Wednesday, October 19, 2005 9:27:33 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   DotNet  | 
 Monday, October 17, 2005
Generating Test Skeletons by phildenoncourt
Nowadays there is a lot of discussion about TDD (Test Driven Development). While I think that anything that forces developers to pay more attention to testing their code is a good thing, I am not convinced that TDD is going to reduce my defect rate. My methodology is to write the code for a problem, document it, ruggedize (add range checks, errorhandling, and verify that resources will always be released), and then write unit tests.

When I talk about unit tests to developers, with the goal of trying to get them to place their unit tests in a unit testing framework like nUnit, there is a tendency for them to balk. "You want me to spend time writing code that an end user will never run" (read - Can I bill for that?), or "That would mean we'd have to start managing our previously disposable test harness code" (read - That means if I change my interface, the test code won't compile), or "Who is going to test the test code?" (read - I'm skeptical this will work and I'm trying to get out of doing this)

My experience with the three projects I've implemented full unit testing is that it is well worth the effort. Obviously, there is going to be more overhead when building the first version. However, it has saved me tremendous time when deploying the 2nd, 3rd, 4th.... time. Not only am I able to test the new features quickly, I can run the entire battery to make sure that I didn't break something when adding a new feature.

There is a utility called "Reflector Graph" that writes test skeletons for you. It is an addin to reflector. To generate a test skeleton, make sure you add the addin to reflector. Find the class you want to create a test for; right click and select Code Generation. On the drop down, select which type of object you are interested in.
Monday, October 17, 2005 5:23:57 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   DotNet  | 
 Friday, October 07, 2005

I've given a introductory talk on Code Access Security a few times now. As I'm showing all the pieces from an administrative point of view (Code Groups, PermissionSets), and I go to create a code group, there is an option in the wizard that allows you to import the settings from an XML file. People listening to my talk always, without fail, ask, "How can I generate that XML file". It's fustrating, because there is no clear way to do. No menu option, no command line utility. I alway mean to research how to do it, but never seem to find the time. A couple of nights ago, I looked into it. It turns out on each PermissionSet, or CodeGroup, there is a ToXml method. Since it appears the only way to get the XML is using that method, I wrote a utility that will export the XML to a file. You can download it here.

Friday, October 07, 2005 1:09:30 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   Development | DotNet  | 
 Tuesday, October 04, 2005
Random.Next by phildenoncourt
Arg!! Serves me right for not reading the documentation and assuming the way a component works. I have a test framework that I use to generate random data when running unit tests on the data layer. I don't care what the data looks like as long as data is in fact saved. I use Random.Next to get a value that I use for a variety of purposes: Getting a letter, a number, or a boolean value. To get a random boolean value, I was using this code:
System.Random r = new Random();
bool tstResult = r.Next(0,1) ==0 ? true : false;
The problem is that Random.Next will return a value >= the first parameter and < the second parameter. So you can see that my routine always returns true. I had assumed that the arguments were the range of numbers you were interested in.
		
Tuesday, October 04, 2005 3:00:25 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   DotNet  | 
 Monday, October 03, 2005
XML Overrides by phildenoncourt
There are a lot of areas in the .NET framework that I haven't paid attention to because I don't have come across a need for a particular area (the WMI stuff) and/or the area looked somewhat complicated. In the XML Serialization engine, there is support for "Overrides". The serialization that I had done to date had worked fine, so I glossed over these objects when I was doing research.

I've written a data layer for my current project. It's a pretty robust data layer if I do say so myself. It has support for parent/child relationships, data transactions, delay loading, cascading deletes, exports to datasets and it's fully generated using a code generator (
MyGeneration).... It's really the Cadillac of data layers.

When an object is serialized, I prevent the serialization of child objects by adding the [XMLIgnore] attribute to the child collection properties. I do this for performance and size reasons. Because the data layer is
delay loaded, the collection might not be populated yet, causing tremendous database activity when a developer decides to serialize the object. Also, if you serialize an object high in the hierarchy, you would end up getting a hugely sized piece of XML. The children of the children of the children would be included in the document. After patting myself on the back for writing a fully featured, lightweight, fast and consistent data layer quickly with relatively few defects, the need came down for the objects to include all of their children in special circumstances... But only select child objects, not all.

I gave this some thought over the weekend and resigned myself to the fact that I was going to have to write some specialized method that serialized each object individually and then molded all the results into one big XML document. After doing some more reading, I came across the
XmlAttributeOverrides object. This object allows you to selectively override XML serialization attributes that you set for specific properties. Using this object, I created a serializer, told it which additional properties I wanted serialized, and BAM!.. Done.


			//Set up overrides
XmlAttributeOverrides overrides = new XmlAttributeOverrides();

//ChildItems
XmlAttributes dontIgnore = new XmlAttributes();
dontIgnore.XmlIgnore=
false;
overrides.Add(
typeof(ParentObject),"ChildItems",dontIgnore); XmlSerializer xs = new XmlSerializer(typeof(ParentObject),overrides);
Monday, October 03, 2005 8:25:06 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   Development | DotNet  | 
 Saturday, October 01, 2005
AssemblyInfo.cs by phildenoncourt
I was troubleshooting some versioning problems we were having with a built; checking versions, strong name, modified dates... One of the assemblies I came across had an assemblyversion of 0.0.0.0 . I thought that was kind of funny, normally you see files with 1.0.0.0 or 0.9.0.0 or 1.0.12424.53264 . After researching it, the reason this assembly had zeros for the version is because the developer removed AssemblyInfo.cs from the project. This caused the assembly to be built without the AssemblyVersion attribute, leaving it with the default 0.0.0.0 version.
Saturday, October 01, 2005 10:16:43 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   Development | DotNet  | 
 Friday, September 30, 2005
Switch statements in IL by phildenoncourt
Just an interesting observation that I found when looking at some code using reflector. If you have a switch statement on a string, and there is a small set of cases, (<~10), the switch is changed to a series of if/else statements. If your list is more than 10, it creates a hashtable, and inserts all the strings. Then using the expression value it find the index of the value in the hashtable and uses that as its key. I'm sure this is done for a performance reason, but I couldn't speculate as to what it was. By looking at this, it would seem to be that it is best to use enumerations, rather than hardcoded strings wherever possible.

Decompiled Switch statement with 5 cases
public string SelectILTest5(string input)
{
      string text2;
      if ((text2 = input) != null)
      {
            text2 = string.IsInterned(text2);
            if (text2 != "a1")
            {
                  if (text2 == "a2")
                  {
                        return "a2";
                  }
                  if (text2 == "a3")
                  {
                        return "a3";
                  }
                  if (text2 == "a4")
                  {
                        return "a4";
                  }
                  if (text2 == "a5")
                  {
                        return "a5";
                  }
            }
            else
            {
                  return "a1";
            }
      }
      return "";
}
Decompiled Switch statement with 15 cases(C#)
public string SelectILTest15(string input)
{
      switch (input)
      {
            case "a1":
            {
                  return "a1";
            }
            case "a2":
            {
                  return "a2";
            }
            case "a3":
            {
                  return "a3";
            }
            case "a4":
            {
                  return "a4";
            }
            case "a5":
            {
                  return "a5";
            }
            case "a6":
            {
                  return "a6";
            }
            case "a7":
            {
                  return "a7";
            }
            case "a8":
            {
                  return "a8";
            }
            case "a9":
            {
                  return "a9";
            }
            case "a10":
            {
                  return "a10";
            }
            case "a11":
            {
                  return "a11";
            }
            case "a12":
            {
                  return "a12";
            }
            case "a13":
            {
                  return "a13";
            }
            case "a14":
            {
                  return "a14";
            }
            case "a15":
            {
                  return "a15";
            }
      }
      return "";
}
Decompiled Switch statement with 15 cases (IL)
.method public hidebysig instance string SelectILTest15(string input) cil managed
{
      // Code Size: 524 byte(s)
      .maxstack 4
      .locals init (
            string text1,
            object obj1)
      L_0000: volatile 
      L_0002: ldsfld [mscorlib]System.Collections.Hashtable <PrivateImplementationDetails>::$$method0x6000015-1
      L_0007: brtrue L_0124
      L_000c: ldc.i4.s 30
      L_000e: ldc.r4 0.5
      L_0013: newobj instance void [mscorlib]System.Collections.Hashtable::.ctor(int32, float32)
      L_0018: dup 
      L_0019: ldstr "a1"
      L_001e: ldc.i4.0 
      L_001f: box int32
      L_0024: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_0029: dup 
      L_002a: ldstr "a2"
      L_002f: ldc.i4.1 
      L_0030: box int32
      L_0035: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_003a: dup 
      L_003b: ldstr "a3"
      L_0040: ldc.i4.2 
      L_0041: box int32
      L_0046: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_004b: dup 
      L_004c: ldstr "a4"
      L_0051: ldc.i4.3 
      L_0052: box int32
      L_0057: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_005c: dup 
      L_005d: ldstr "a5"
      L_0062: ldc.i4.4 
      L_0063: box int32
      L_0068: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_006d: dup 
      L_006e: ldstr "a6"
      L_0073: ldc.i4.5 
      L_0074: box int32
      L_0079: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_007e: dup 
      L_007f: ldstr "a7"
      L_0084: ldc.i4.6 
      L_0085: box int32
      L_008a: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_008f: dup 
      L_0090: ldstr "a8"
      L_0095: ldc.i4.7 
      L_0096: box int32
      L_009b: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_00a0: dup 
      L_00a1: ldstr "a9"
      L_00a6: ldc.i4.8 
      L_00a7: box int32
      L_00ac: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_00b1: dup 
      L_00b2: ldstr "a10"
      L_00b7: ldc.i4.s 9
      L_00b9: box int32
      L_00be: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_00c3: dup 
      L_00c4: ldstr "a11"
      L_00c9: ldc.i4.s 10
      L_00cb: box int32
      L_00d0: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_00d5: dup 
      L_00d6: ldstr "a12"
      L_00db: ldc.i4.s 11
      L_00dd: box int32
      L_00e2: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_00e7: dup 
      L_00e8: ldstr "a13"
      L_00ed: ldc.i4.s 12
      L_00ef: box int32
      L_00f4: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_00f9: dup 
      L_00fa: ldstr "a14"
      L_00ff: ldc.i4.s 13
      L_0101: box int32
      L_0106: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_010b: dup 
      L_010c: ldstr "a15"
      L_0111: ldc.i4.s 14
      L_0113: box int32
      L_0118: call instance void [mscorlib]System.Collections.Hashtable::Add(object, object)
      L_011d: volatile 
      L_011f: stsfld [mscorlib]System.Collections.Hashtable <PrivateImplementationDetails>::$$method0x6000015-1
      L_0124: ldarg.1 
      L_0125: dup 
      L_0126: stloc.1 
      L_0127: brfalse L_0202
      L_012c: volatile 
      L_012e: ldsfld [mscorlib]System.Collections.Hashtable <PrivateImplementationDetails>::$$method0x6000015-1
      L_0133: ldloc.1 
      L_0134: call instance object [mscorlib]System.Collections.Hashtable::get_Item(object)
      L_0139: dup 
      L_013a: stloc.1 
      L_013b: brfalse L_0202
      L_0140: ldloc.1 
      L_0141: unbox int32
      L_0146: ldind.i4 
      L_0147: switch (L_018a, L_0192, L_019a, L_01a2, L_01aa, L_01b2, L_01ba, L_01c2, L_01ca, L_01d2, L_01da, L_01e2, L_01ea, L_01f2, L_01fa)
      L_0188: br.s L_0202
      L_018a: ldstr "a1"
      L_018f: stloc.0 
      L_0190: br.s L_020a
      L_0192: ldstr "a2"
      L_0197: stloc.0 
      L_0198: br.s L_020a
      L_019a: ldstr "a3"
      L_019f: stloc.0 
      L_01a0: br.s L_020a
      L_01a2: ldstr "a4"
      L_01a7: stloc.0 
      L_01a8: br.s L_020a
      L_01aa: ldstr "a5"
      L_01af: stloc.0 
      L_01b0: br.s L_020a
      L_01b2: ldstr "a6"
      L_01b7: stloc.0 
      L_01b8: br.s L_020a
      L_01ba: ldstr "a7"
      L_01bf: stloc.0 
      L_01c0: br.s L_020a
      L_01c2: ldstr "a8"
      L_01c7: stloc.0 
      L_01c8: br.s L_020a
      L_01ca: ldstr "a9"
      L_01cf: stloc.0 
      L_01d0: br.s L_020a
      L_01d2: ldstr "a10"
      L_01d7: stloc.0 
      L_01d8: br.s L_020a
      L_01da: ldstr "a11"
      L_01df: stloc.0 
      L_01e0: br.s L_020a
      L_01e2: ldstr "a12"
      L_01e7: stloc.0 
      L_01e8: br.s L_020a
      L_01ea: ldstr "a13"
      L_01ef: stloc.0 
      L_01f0: br.s L_020a
      L_01f2: ldstr "a14"
      L_01f7: stloc.0 
      L_01f8: br.s L_020a
      L_01fa: ldstr "a15"
      L_01ff: stloc.0 
      L_0200: br.s L_020a
      L_0202: ldstr ""
      L_0207: stloc.0 
      L_0208: br.s L_020a
      L_020a: ldloc.0 
      L_020b: ret 
}
Friday, September 30, 2005 4:44:56 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   Development | DotNet  | 
Copyright © 2009 Phil Denoncourt III. All rights reserved.
DasBlog 'Portal' theme by Johnny Hughes.
Pick a theme: