His code was
stylish, sexy and had very few lines. So I learnt LINQ and other new features
at that time. After learning LINQ and generics, I had knowledge of the new
feature existence but very few features are used everyday, so I put together 7
things that make our life easier.
People who have worked in 2.0 and earlier
version will like this 7 things more.
Let Keyword
LET can be
used inside the LINQ query. It can be used in places where you need to have EXPRESSION THAT CAN BE REUSED
Syntax: let identifier = expression
int[] array = { 1, 3, 5, 7, 9 }; var result = from element in array where element * 100 >= 500 select element * 100; foreach (var res in result) Console.WriteLine(res);
int[] array = { 1, 3, 5, 7, 9 }; var result = from element in array let v = element * 100 where v >= 500 select v; foreach (var res in result) Console.WriteLine(res);
In the
above example the value of the array element has to be multiplied by 100 and its
value should also be displayed and should also be used to filter items
which is greater than 500. element
* 100 is used in many places. "LET" comes to our rescue to handle
this situation of using the expression without duplicating everywhere.
Delegate or Lambda - Passing function as argument
Whenever we want to have certain set
of operation that needs to be delegated (REMEMBER STRATEGY PATTERN) to
different object we use the delegates. In C# we have delegate key word for
passing the function as a parameter. We can use lambda expression too.
Example:
Let
us have a delegate which accepts two Boolean variable and returns integer
value. For people new to delegates, think delegate as a function pointer in C
world, where it can save the address of any function that satisfies the input
parameters and return type (SAME SIGNATURE).
In below case any function which has
two Boolean input parameters and has an integer return type can be assigned to
DiscountDelegate.
delegate int DiscountDelegate(bool x,bool y);
Let’s have a class that consumes the
DiscountDelegate which has method named Process whose
parameter is DiscountDelegate.
In
this method Process, discount is invoked, what happens here below is that the call is
placed on the address of the discount and whatever the method resolves to the
code will be executed.
class ShoppingCart{ public void Process(DiscountDelegate discount) { int magicDiscount = discount(true,false); } }
Now we will define a Calculate and Calculate1 functions which takes two parameters and return the integer value as below. The method below can be assigned to the DiscountDelegate since it satisfies the condition of the DiscountDelegate signature.
class Calculator { public static int Calculate(bool marketing, bool sales) { int discount = 0; if (marketing) { discount = 5; } else if (sales) { discount = 10; } else { discount = 15; } return discount; } public static int Calculate2(bool marketing, bool sales) { int discount = 0; if (marketing) { discount = 25; } else if (sales) { discount = 30; } else { discount = 23; } return discount; } }
Since
Calculate1 and Calculate2 satisfy the criteria of DiscountDelegate with two
bool parameters and return type is integer, we can assign the Calculate to the
discount.
public static void DelegateExec() { new ShoppingCart().Process(new DiscountDelegate(Calculator.Calculate)); new ShoppingCart().Process(new DiscountDelegate(Calculator.Calculate2)); }
Or we can use the lambda expression
to have a better implementation of the code, check out the syntax below where the method definition is added while calling the Process method, which reduces the reusablility but the structure helps to define the implementation easily
new ShoppingCart().Process((marketing, sales) => { int discount = 0; if (marketing) { discount = 5; } else if (sales) { discount = 10; } else { discount = 15; } return discount; });
Above code reduces class creation or
method that has the actual implementation which decreases lots of classes
piling up in our library. Welcome to the lambda world where your class file and
lines of code gets reduced.
Type Parameter
First let’s understand where we need Type
parameters
- Type parameters are used in places where there is need of heavy boxing activities.
- It is used in places where you need to define a variable of type that changes as per the object that is calling it.
To understand
more let’s take an example of saving a data in a two different forms.
·
XML file
·
Normal text file.
In normal case we might have to create a
common class that will take care of the type of class that needs to be created
to pass the instance as parameter and have a variable instantiated based on the
type that has been passed. This requires the variable to be instantiated in the
calling side before passing this instantiation or must be taken care in the server
side or any Factory method to create an instance which requires boxing. Instead
we use the type parameter.
Let us have an
IDataOperation that performs
CRUD operations
public interface IDataOperation { bool Update(string value); bool Insert(string value); bool Delete(string value); }
For XML Operation
public class XMLOperations:IDataOperation { public bool Update(string value) { return true; } public bool Insert(string value) { return true; } public bool Delete(string value) { return true; } }
For Normal Text file operation
public class FileOperation:IDataOperation { public bool Update(string value) { return true; } public bool Insert(string value) { return true; } public bool Delete(string value) { return true; } }
Normal way is to instantiate the FileOperation and XMLOperations in
DataAccess class which
handles the save functionality of the data. Below is typical Factory method for creating an instance.
public class DataAccess { public IDataOperation dataOperation { get; set; } public DataAccess(string type) { if (type == "XML") { dataOperation = new FileOperation(); } else if (type == "file") { dataOperation = new XMLOperations(); } } }
Instead we can use the type parameter class and the instance will be created at run time based on the caller.
public class DataAccess: IDataOperation where T : IDataOperation { public T dataOperation { get; set; } public DataAccess() { dataOperation = Activator.CreateInstance (); } public bool Update(string value) { return dataOperation.Update(value); } public bool Insert(string value) { return dataOperation.Insert(value); } public bool Delete(string value) { return dataOperation.Delete(value); } }
In the above
example the T is parameter of type IDataOperation and so it will be evaluated at runtime, which saves the
overhead of creating the instance and boxing those instances. So to consume the methods, you
need to send the dataOperation type when creating the instance of the
object as Type Parameter.
DataAccessxml = new DataAccess ();
private static void doXMLOperations() { DataAccessxml = new DataAccess (); xml.Insert("Input"); xml.Update("Input"); xml.Delete("Input"); } private static void doFileOperations() { DataAccess file = new DataAccess (); file.Insert("Input"); file.Update("Input"); file.Delete("Input"); }
Thus we get the
value of the object at run time and no boxing required, which saves lots of
code and also the code looks elegant.
Anonymous Type
This is one of the sweet features of
C# 3.5. When we need set of properties that needs to be used only once without
having to create a new class file which contains the properties.
Syntax for the
anonymous method is pretty simple and self-explanatory.
return new { Name = "apple", Age = 2, Weight = 150.50 };
Coolest thing about the anonymous type is
we don’t have to describe any type of the property that is defined in the
anonymous type. It is resolved @ runtime, which will be very useful when we
need to pass value to another interface whose information is not known. Above
example can be used in passing data to web layer as it is and can be consumed
by Ajax call as a JSON object after normal data contract serialization.
Co-Variance and Contra-Variance
First, let’s see
what is Variance, Variance is about
being able to use an object of one type as if it were another in a type safe
way.
Covariance
is all about values being returned from an operation back to the caller. Think
of Covariance as OUT modifier. In the below example the string is defined and
its value is being assigned into less derived type
//
Covariance
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type.
object obj = str;
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type.
object obj = str;
Contra variance
is opposite way around, it’s about values being passed by the caller and callee
consumes the value instead of producing them. Think of Contra-variance as IN
Modifier.
//
Assignment compatibility.
object obj = "test";
string str = obj;
object obj = "test";
string str = obj;
In above
example, an object that is instantiated with a more derived type argument and is
assigned to an object instantiated with a less derived type argument.
Why I need to know about variance.
We need to know about variance is
for one reason, how the compiler will behave in a particular scenario. Whether
it will compile or it throws runtime exception for particular situation. In the
below list the covariance and contra-variance support exists in the following tabular
list. If you know what type of variance is supported, then you will know what type to consume and which type to assign.
IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;
Since you know IEnumerable<T> is covariant we can assign the
derived class to base component. Knowing these information helps us to know
which to use as an OUT and IN respectively. Below shows the list of Types and
their variances.
If you want, one thing to
remember about variance is, IEnumerable
and Func are covariant, so we can assign derived class to base component.
Type
|
Covariant type parameters
|
Contravariant type parameters
|
Action<T> to Action<T1, T2, T3, T4 >
|
Yes
|
|
Comparison<T>
|
Yes
|
|
Converter<TInput, TOutput>
|
Yes
|
Yes
|
Func<TResult>
|
Yes
|
|
Func<T, TResult> to Func<T1, T2, T3, T4, TResult>
|
Yes
|
Yes
|
IComparable<T>
|
Yes
|
|
Predicate<T>
|
Yes
|
|
IComparer<T>
|
Yes
|
|
IEnumerable<T>
|
Yes
|
|
IEnumerator<T>
|
Yes
|
|
IEqualityComparer<T>
|
Yes
|
|
IGrouping<TKey, TElement>
|
Yes
|
|
IOrderedEnumerable<TElement>
|
Yes
|
|
IOrderedQueryable<T>
|
Yes
|
|
IQueryable<T>
|
Yes
|
|
To understand in depth and its mathematical
notation, click
here
Extension
Extension is one of the cool
features that is available in Advanced C# (3.0 and above). This one functionality has reduced number of
lines of code in every project. For example, if we need to format a string in
the project consistently in many places we need to call a method of static
class that accepts input and gives us the formatted string.Here we can also see using Interface with Extension
Extension solves the problem by having
an class and having all the needed transformation for the data
type that are extended. Any type can be
extended. If the type is extended then the extended method will part of the
type where it will accessible just by referencing its namespace.
Define a static method inside static class and the
first parameter must be this.
public static class
MyExtensions
{
public static string ToCurrenyFormat(this
string val,string
format)
{
var CurrencyMark = format == "IND"
? "Rs": "$";
return string.Format("{0} {1}", val, CurrencyMark);
}
}
First parameter must be this and followed by
<Type> that
needs to extended and it can be followed by any number of input parameters you
want.
In above method, string is being extended
with the name ToCurrenyFormat, look
image below now where you can see the ToCurrenyFormat in the intellisense list.
return “1000”.ToCurrenyFormat("US");
You can also extend interface. Syntax and Concept are
same. Define a static method inside static class and the first parameter must
be this.
public interface ITestInterfaceExtension { string myMethod(); } public static string InterfaceExtension(this ITestInterfaceExtension value) { return "FOO"; }
In the above method we have extended the ITestInterfaceExtension, so going forward when we have to implement the InterfaceExtension for ITestInterfaceExtension. This can be used in places where the interface is got from the source where its source code is not editable, but we want another method to be implemented as part of the code. Extension can be used in places for all types. Since interface is a type, it can also be extended.
Optional Parameter
Optional parameters are one of the new
features in .Net 4.5, which has been a long wait. If you are aware of optional
parameters in SQL server, then this feature exactly does the same here. This avoids overloading methods only reason being a new parameter is needed, just because your business requirement
changed. When optional parameters are defined, old method call will function
as it is and new one can use the updated parameter.
In the below
method value is the
mandatory parameter and value1
is the optional parameter, where we give assign default value to value1
public static bool OptionalParameterMethod(string value, string value1 = null) { return true; }
The trick lies
in calling the optional parameter, the optional parameter must be add to the end
and it cannot supersede the mandatory parameter
public static void CallNamedArgumentMethod(string s) { OptionalParameterMethod("ABC"); OptionalParameterMethod(value1: "ABC", value: "ASDF"); OptionalParameterMethod("ABD", value1: "LKJH"); // Positional arguments cannot follow named arguments. // The following statement causes a compiler error. //OptionalParameterMethod(value1:"ABD", "LKJH"); }
In the first example the OptionalParameterMethod is called with only one parameters and
the s2 is ignored so the value null will be taken.
OptionalParameterMethod(s1:"ABD",
"LKJH"); will
show you a compile error and because the positional argument cannot follow
named argument, so try to give the positional argument at the last and it
should be resolvable or do not provide any value
All the above listed features, such
as Need for covariance and Contra-variance, Extension, Optional parameters, Anonymous
Types, Type Parameters and Let keyword are all my favorite things about
advanced C#, but there are much more than that, but what I have listed is the
one we can use daily in our coding activities. Please comment below, the
feature you find interesting in Advanced C#
Leave a comment to share what you
learned, and hit the “like” button to let others know about it (make sure
you’re logged into Facebook to like and comment).
No comments:
Post a Comment
Note: only a member of this blog may post a comment.