C# Basic (4)

1. Defining and using Properties

class Address
    {
        protected string city;
        protected string zipCode;

        public string ZipCode
        {
            get
            {
                return zipCode;
            }
            set
            {
                // Validate value against some datastore.
                zipCode = value;
                // Update city based on validated zipCode.
            }
        }
    }

Now that we’ve written the Address.ZipCode property, let’s look at the changes needed for the client code:

    Address addr = new Address();
    addr.ZipCode = "55555";
    string zip = addr.ZipCode;

As you can see, how a client accesses the fields is intuitive.

In cases where you have both a getter method and a setter method, if you override one, you must override both.

Besides, it is also the lazy initialization. This is an optimization technique whereby some of a class’s members are not initialized until they are needed.

2.

Declares and instantiates a single-
dimensional array of 6 integers.
int[] numbers = new int[6];

However, when declaring the array as a member of a class, you need to declare and instantiate the array in two distinct steps because you can’t instantiate an object until run time:

using System;

class SingleDimArrayApp
{
    protected int[] numbers;

    SingleDimArrayApp()
    {
        numbers = new int[6];
        for (int i = 0; i < 6; i++)
        {
            numbers[i] = i * i;
        }
    }

    protected void PrintArray()
    {
        for (int i = 0; i < numbers.Length; i++)
        {
            Console.WriteLine("numbers[{0}]={1}", i, numbers[i]);
        }
    }

    public static void Main()
    {
        SingleDimArrayApp app = new SingleDimArrayApp();
        app.PrintArray();
    }
}

System.Array Length property to determine the number of elements in the array. Length property actually returns the number of all the elements in all the dimensions of an array. Therefore, in the case of a two dimensional array of 5 by 4, the Length property would return 9.

the number of dimensions in a declared C# array, count the number of commas and add one to that total.

using System;

class MultiDimArrayApp
{
    protected int currentMonth;
    protected double[,] sales;

    MultiDimArrayApp()

    {
        currentMonth=10; 

        sales = new double[2, currentMonth];
        for (int i = 0; i < sales.GetLength(0); i++)
        {
            for (int j=0; j < 10; j++)
            {
                sales[i,j] = (i * 100) + j;
            }
        }
    }

    protected void PrintSales()
    {
        for (int i = 0; i < sales.GetLength(0); i++)
        {
            for (int j=0; j < sales.GetLength(1); j++)
            {
                Console.WriteLine("[{0}][{1}]={2}", i, j, sales[i,j]);
            }
        }
    }

    public static void Main()
    {
        MultiDimArrayApp app = new MultiDimArrayApp();
        app.PrintSales();
    }
}

The number of dimensions in an array is called an array’s rank, and rank is retrieved using the Array.Rank property.

Console.WriteLine(“doubleD Rank = {0}”, doubleD.Rank);

doubleD Rank = 2;

3. A jagged array is simply an array of arrays.                          int[][]  jaggedArray;

using System; 

class Control
{
    virtual public void SayHi()
    {
        Console.WriteLine("base control class");
    }
}

class Button : Control
{
    override public void SayHi()
    {
        Console.WriteLine("button control");
    }
}

class Combo : Control
{
    override public void SayHi()
    {
        Console.WriteLine("combobox control");
    }
}

class JaggedArrayApp
{
    public static void Main()
    {
    Control[][] controls;
    controls = new Control[2][];

    controls[0] = new Control[3];
    for (int i = 0; i < controls[0].Length; i++)
    {
        controls[0][i] = new Button();
    }

    controls[1] = new Control[2];
    for (int i = 0; i < controls[1].Length; i++)
    {
        controls[1][i] = new Combo();
    }

    for (int i = 0; i < controls.Length;i++)
    {
        for (int j=0;j< controls[i].Length;j++)
        {
            Control control = controls[i][j];
            control.SayHi();
        }
    }        

    string str = Console.ReadLine();
    }
}

4. Indexer Example

using System;
using System.Collections;

class MyListBox

{
    protected ArrayList data = new ArrayList();

    public object this[int idx]
    {
        get
        {
            if (idx > -1 && idx < data.Count)
            {
                return (data[idx]);
            }
            else
            {
                // Possibly throw an exception here.
                return null;
            }
        }
        set
        {
            if (idx > -1 && idx < data.Count)
            {
                data[idx] = value;
            }
            else if (idx == data.Count)
            {
                data.Add(value);
            }
            else
            {
                // Possibly throw an exception here.
            }
        }
    }
}

class Indexers1App
{
    public static void Main()
    {
        MyListBox lbx = new MyListBox();
        lbx[0] = "foo";
        lbx[1] = "bar";
        lbx[2] = "baz";
        Console.WriteLine("{0} {1} {2}",
                           lbx[0], lbx[1], lbx[2]);
    }

 

 

5.  Attributes provide you a generic means of associating information (as annotations) with your defined C# types. you have an application that stores some of its information in the Windows Registry. One design issue would be deciding where to store the Registry key information. In most development environments, this information is typically stored in a resource file or in constants or it’s even hard-coded in the calls to the Registry APIs. However, once again, what we have is a situation in which an integral part of a class is being stored apart from the rest of the class’s definition. Using attributes, we could “attach” this information to the class members such that we have a completely self-describing component.

class MyClass
{
    [RegistryKey(HKEY_CURRENT_USER, "foo")]
    public int Foo;
}

 

6.

Class Attributes:

using System; 

public enum RemoteServers
{
    JEANVALJEAN,
    JAVERT,
    COSETTE    
}

public class RemoteObjectAttribute : Attribute
{
    public RemoteObjectAttribute(RemoteServers Server)
    {
        this.server = Server;
    }

    protected RemoteServers server;
    public string Server
    {
        get 
        { 
            return RemoteServers.GetName(typeof(RemoteServers),
                                         this.server);
        }
    }
}

[RemoteObject(RemoteServers.COSETTE)]
class MyRemotableClass
{
}

To determine the server on which to create the object, use code like the following:

class ClassAttrApp
{
    public static void Main()
    {
        Type type = typeof(MyRemotableClass);
        foreach (Attribute attr in type.GetCustomAttributes())
        {
            RemoteObjectAttribute remoteAttr = 
                attr as RemoteObjectAttribute;
            if (null != remoteAttr)
            {
                Console.WriteLine("Create this object on {0}.",
                                  remoteAttr.Server);
            }
        }
    }
}

As you might expect, the output from this application is the following:

Create this object on COSETTE.

Method Attribute:

using System; 
using System.Reflection;

public class TransactionableAttribute : Attribute
{
    public TransactionableAttribute()
    {
    }
}

class TestClass
{
    [Transactionable]
    public void Foo()
    {}

    public void Bar()
    {}

    [Transactionable]
    public void Baz()
    {}
}

class MethodAttrApp
{
    public static void Main()
    {
        Type type = Type.GetType("TestClass");
        foreach(MethodInfo method in type.GetMethods())
        {
            foreach (Attribute attr in
                     method.GetCustomAttributes())
            {
                if (attr is TransactionableAttribute)
                {
                    Console.WriteLine("{0} is transactionable.", 
                                      method.Name);
                }
            }
        }
    }
}

The code outputs the following:

Foo is transactionable.
Baz is transactionable.

Notice that when attaching an attribute with a constructor that takes no parameters, you do not need to include the open and closed parentheses.

Field attributes:

using System; 
using System.Reflection;

public enum RegistryHives
{
    HKEY_CLASSES_ROOT,
    HKEY_CURRENT_USER,
    HKEY_LOCAL_MACHINE,
    HKEY_USERS,
    HKEY_CURRENT_CONFIG
}

public class RegistryKeyAttribute : Attribute
{
    public RegistryKeyAttribute(RegistryHives Hive, String ValueName)
    {
        this.Hive = Hive;
        this.ValueName = ValueName;
    }

    protected RegistryHives hive;
    public RegistryHives Hive
    {
        get { return hive; }
        set { hive = value; }
    }

    protected String valueName;
    public String ValueName
    {
        get { return valueName; }
        set { valueName = value; }
    }
}

class TestClass

{
    [RegistryKey(RegistryHives.HKEY_CURRENT_USER, "Foo")]
    public int Foo;

    public int Bar;
}

class FieldAttrApp
{
    public static void Main()
    {
        Type type = Type.GetType("TestClass");
        foreach(FieldInfo field in type.GetFields())
        {
            foreach (Attribute attr in field.GetCustomAttributes())
            {
                RegistryKeyAttribute registryKeyAttr = 
attr as RegistryKeyAttribute;
                if (null != registryKeyAttr)
                {
                    Console.WriteLine
                        ("{0} will be saved in {1}\\\\{2}", 
                         field.Name, 
                         registryKeyAttr.Hive, 
                         registryKeyAttr.ValueName);
                }
            }
        }
    }
}

 

7.

Positional parameters are parameters to the attribute’s constructor. They are mandatory and must be specified every time the attribute is used. In the RegistryKeyAttribute example above, Hive and ValueName are both positional parameters.

Named parameters are actually not defined in the attribute’s constructor—rather, they are nonstatic fields and properties. Therefore, named parameters give the client the ability to set an attribute’s fields and properties when the attribute is instantiated without you having to create a constructor for every possible combination of fields and properties that the client might want to set.

public RegistryKeyAttribute(String ValueName)

The user can now attach the attribute in either of the following ways:

[RegistryKey("Foo")]
[RegistryKey("Foo", Hive = RegistryHives.HKEY_LOCAL_MACHINE)]

This gives you the flexibility of having a default value for a field while at the same time giving the user the ability to override that value if needed.

When using named parameters, you must specify the positional parameters first. After that, the named parameters can exist in any order because they’re preceded by the name of the field or property. The following, for example, will result in a compiler error:

// This is an error because positional parameters can't follow
// named parameters.
[RegistryKey(Hive=RegistryHives.HKEY_LOCAL_MACHINE, "Foo")]

Additionally, you can’t name positional parameters. When the compiler is compiling an attribute’s usage, it will attempt to resolve the named parameters first. Then it will attempt to resolve what’s left over—the positional parameters—with the method signature. The following will not compile because the compiler can resolve each named parameter but, when finished with the named parameters, it can’t find any positional parameters and states that ” No overload for method ‘RegistryKeyAttribute’ takes ‘0” arguments “:

[RegistryKey(ValueName="Foo", Hive=RegistryHives.HKEY_LOCAL_MACHINE)]

Lastly, named parameters can be any publicly accessible field or property—including a setter method—that is not static or const.

The types of positional parameters and named parameters for an attribute class are limited to the attribute parameter types, which are listed here:

  • bool, byte, char, double, float, int, long, short, string
  • System. Type
  • · object
  • An enum type, provided that it and any types in which it is nested are publicly accessible—as in the example used with the RegistryHives enumeration
  • A one-dimensional array involving any of the types listed above

7. AttributeUsage

addition to custom attributes that you use to annotate regular C# types, you can use the AttributeUsage attribute to define how you want these attributes to be used. The AttributeUsage attribute has the following documented calling conventions:

[AttributeUsage( 
validon, 
AllowMultiple = allowmultiple, 
Inherited = inherited
)]

Actually, the validon parameter in the AttributeUsage attribute is of the type AttributeTargets, which is an enumerationdefined as follows:

public enum AttributeTargets
{
   Assembly    = 0x0001,
   Module      = 0x0002,
   Class       = 0x0004,
   Struct      = 0x0008,
   Enum        = 0x0010,
   Constructor = 0x0020,
   Method      = 0x0040,
   Property    = 0x0080,
   Field       = 0x0100,
   Event       = 0x0200,
   Interface   = 0x0400,
   Parameter   = 0x0800,
   Delegate    = 0x1000,
   All = Assembly | Module | Class | Struct | Enum | Constructor | 
         Method | Property | Field | Event | Interface | Parameter | 
         Delegate,

   ClassMembers  =  Class | Struct | Enum | Constructor | Method | 
                  Property | Field | Event | Delegate | Interface,
}
[AttributeUsage(AttributeTargets.Class)]
public class RemoteObjectAttribute : Attribute]
{
    ...
}

[AttributeUsage(AttributeTargets.Method)]
public class TransactionableAttribute : Attribute
{
    ...
}

[AttributeUsage(AttributeTargets.Field)]
public class RegistryKeyAttribute : Attribute
{
    ...
}

One last point regarding the AttributeTargets enumeration:you can combine members by using the | operator. If you have an attribute that would apply to both fields and properties, you would attach the AttributeUsage attribute to it as follows:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]

You can use AttributeUsage to define attributes as either single-use or multiuse. This decision pertains to how many times a single attribute can be used on a single field. By default, all attributes are single-use, meaning that compiling the following results in a compiler error:

using System; 
using System.Reflection;

public class SingleUseAttribute : Attribute
{
    public SingleUseAttribute(String str)
    {
    }
}

// ERROR: This results in a "duplicate attribute" compiler error.
[SingleUse("abc")]
[SingleUse("def")]
class MyClass
{
}

class SingleUseApp
{
    public static void Main()
    {
    }
}

To fix this problem, specify on the AttributeUsage line that you want to allow the attribute to be attached multiple times to a given type. This would work:

using System; 
using System.Reflection;

[AttributeUsage(AttributeTargets.All, AllowMultiple=true)]
public class SingleUseAttribute : Attribute
{
    public SingleUseAttribute(String str)
    {
    }
}

[SingleUse("abc")]
[SingleUse("def")]
class MyClass
{

}

class MultiUseApp
{
    public static void Main()
    {
    }
}

To use an attribute identifier, preface the attribute name with the identifier and a colon. In the MyClass example, if you wanted to make sure the compiler could determine that the HRESULT attribute is meant to annotate the return value and not the method, you would specify it as follows:

class MyClass
{
    [return:HRESULT]
    public long Foo();
}
Advertisements
This entry was posted in C#. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s