Tuesday, April 29, 2014

.Net Listing Classes or Methods With A Particular Attribute.

Whenever I build a website I always like to build an admin section and expose information such as which actions are cached, which controllers require authorization and which fields are required etc. Luckily in .Net MVC most of these things are implemented as attributes.

Here is an example of scanning an assembly and listing all the classes and methods with a particular attribute on them. I chose two random attributes for this example:

[Obsolete]
public class Program
{
    public static void Main(string[] args)
    {
        var program = new Program();
        var types = program.GetAttributesOnClasses();
        foreach (var t in types)
        {
            Console.WriteLine("Class: {0}  - Attribute: {1}", t.Type.Name, t.Attributes);
        }

        var methods = program.GetAttributesOnMethods();
        foreach (var method in methods)
        {
            Console.WriteLine("Method Name: {0} -  Attribute: {1}", method.MethodInfo.Name, method.Attributes);
        }

        Console.ReadLine();
    }
  
    public IEnumerable GetAttributesOnClasses<T>() where T : Attribute
    {
        return new AttributeScan().GetTypesWith<T>(new[] { Assembly.GetExecutingAssembly() }, false);
    }

    public IEnumerable<methodattributeinfo> GetAttributesOnMethods<T>() where T : Attribute
    {
        return new AttributeScan().GetMethodsWith<T>(new[] { Assembly.GetExecutingAssembly() }, false);
    }

    [STAThread]    
    public void DoNothing()
    {          
    }

    [STAThread]
    private void PrivateDoNothing()
    {
    }

}
Will output:

Class: Program  - Attribute: System.ObsoleteAttribute[]
Method Name: DoNothing -  Attribute: System.STAThreadAttribute[]

Note that the scan did not take into account the private method.

Friday, April 25, 2014

Why don't more people use the .Net Settings as opposed to Config settings.

I see a lot of people putting key-value pairs in web.config/app.config files and then using them in their applications via
var someConfigKey = ConfigurationManager.AppSettings["someConfigKey"];
int count;
if (int.TryParse(someConfigKey, out count))
{
    //// Do Something
}

However I rarely see people using application settings. I won't go into depth on what application settings are as the Microsoft website has a pretty good explanation of them here however Settings are strongly typed and so the above lines of code can all be replaced with:
var count = Properties.Settings.Default.Count;

I might be missing something here but it seems like the latter is much more convenient to use if you are simply using the appSettings section of the .config file. Of course it is a different story if you are using .config sections which are serialized into objects but for the most part what I can figure is that many people don't know about .net Settings.

Take this or this stackoverflow post which add to the confusion and were found on the first page of googling for the difference between settings and config files.

As a side note, if you do choose to use .Net Settings instead of a .Config file and you wanted to list your settings on some admin section of a webpage you could do something like so:
public Dictionary GetSettings()
{
  return Properties.Settings.Default.Properties.Cast<SettingsProperty>()
    .ToDictionary(property => property.Name, property => Properties.Settings.Default[property.Name].ToString());
}

Friday, April 11, 2014

VB.Net Gotchas

VB.NET is a very user friendly language however I find its user friendliness to be quite confusing and error prone, especially when switching back and forth between C# and VB. Simple things like VB's handling of null or concatenation can lead to bugs if your not careful. For example:
 
   Sub Main()

        Console.WriteLine("The value of Nothing is also the value of the default type: " &
                         (Nothing = False))

        Console.WriteLine("The value of Nothing is also the value of the default type: " &
                          (Nothing = 0))

        Console.WriteLine("The value of Nothing is also the value of the default type: " &
                          (Nothing = String.Empty))

        Console.WriteLine("The & operator with two integers concats both: " & (3 & 4))

        Console.WriteLine("The + operator with two integers adds both: " & (3 + 4))

        Console.WriteLine("The + operator with two strings concats both: " & ("3" + "4"))

        Console.WriteLine("The & operator with a string and an integer concats both: " & 
                          ("3" & 4))

        Console.WriteLine("The + operator with a string and an integer adds both: " & 
                          ("3" + 4))

        Console.WriteLine("The + operator with an integer and string adds both: " & 
                          (4 + "3"))
        
        Console.WriteLine("The Length of a VB.NET array starts at 1 and not 0: " &
                          {"Dot", "Net", "Perls"}.Length)

        'Console.WriteLine("DirectCast can NOT convert types that are not inherited: " & 
        '               DirectCast(0, System.String))
        'System.IndexOutOfRangeException' 

        Console.WriteLine("CType can convert types that are not inherited: " &
                       CType(0, System.String))

        'Console.WriteLine("Nothing <> String: " & (Nothing = String.Empty))
        'System.IndexOutOfRangeException' 

        Console.WriteLine("Nothing is not string.Empty: " & (Nothing IsNot String.Empty))

        'Console.WriteLine("And operator always evaluates all conditions: " & 
        '                 (1 = 2 And 0 = String.Empty))
        'System.InvalidCastException

        Console.WriteLine("AndAlso operator evaluates left to right conditions as needed: " &
                          (1 = 2 AndAlso 0 = String.Empty))

        Console.WriteLine("The value of TestFunctionExitWithType is going to be the default" &
                          " return type: " & TestFunctionExitWithType())

        Console.WriteLine("The value of TestFunctionReturn is going to be Nothing: " &
                          TestFunctionReturn())

        Console.WriteLine("The value of TestFunctionExit is going to be Nothing: " &
                          TestFunctionExit())

        Console.WriteLine("Parenthesis are optional in Paremeterless methods: " & 
                          0.ToString)

        Console.ReadLine()

    End Sub

    Sub TestSubReturn()
        Return
    End Sub

    Sub TestSubExit()
        Exit Sub
    End Sub

    Function TestFunctionReturn()
        Return Nothing
    End Function

    'Compiler error: 'Return' statement in a Function, Get, or Operator must return a value.
    'Function TestFunctionReturn()
    '    Return
    'End Function

    Function TestFunctionExit()
        Exit Function
    End Function

    Function TestFunctionExitWithType() As Decimal
        Exit Function
    End Function

End Sub
Results:
The value of Nothing is also the value of the default type: True
The value of Nothing is also the value of the default type: True
The value of Nothing is also the value of the default type: True
The & operator with two integers concats both: 34
The + operator with two integers adds both: 7
The + operator with two strings concats both: 34
The & operator with a string and an integer concats both: 34
The + operator with a string and an integer adds both: 7
The + operator with an integer and string adds both: 7
The Length of a VB.NET array starts at 1 and not 0: 3
CType can convert types that are not inherited: 0
Nothing is not string.Empty: True
AndAlso operator evaluates left to right conditions as needed: False
The value of TestFunctionExitWithType is going to be the default return type: 0
The value of TestFunctionReturn is going to be Nothing: 
The value of TestFunctionExit is going to be Nothing: 
Parenthesis are optional in Paremeterless methods: 0