Sexy C#
1. INTRODUCTION
C# IS A VERY POPULAR PROGRAMMING LANGUAGE. IT IS MOSTLY POPULAR IN THE .NET ARENA. THE MAIN REASON BEHIND THAT IS THE C# LANGUAGE CONTAINS SO MANY USEFUL FEATURES. IT IS ACTUALLY A MULTI-PARADIGM PROGRAMMING LANGUAGE.
Q. WHY DO WE CALL C# A MUTI-PARADIGM PROGRAMMING LANGUAGE?
A. WELL, C# HAS THE FOLLOWING CHARACTERISTICS:
- STRONGLY TYPED
- OBJECT ORIENTED
- FUNCTIONAL
- DECLARATIVE PROGRAMMING
- IMPERATIVE PROGRAMMING
- COMPONENT BASED PROGRAMMING
- DYNAMIC PROGRAMMING
SO WE CAN SAY IT IS MULTI- PARADIGM PROGRAMMING LANGUAGE. C# HAS MANY INTERESTING FEATURES THAT SEEMS SO HOT AND SEXY. FOR THAT REASON WE CAN SAY IT IS A SEXY LANGUAGE TOO. IN THIS ARTICLE I WILL EXPLAIN ONE BY ONE OF THOSE HOT AND SEXY FEATURES WITH CODE EXAMPLE. IN SOME SECTIONS I USE QUESTION(Q) AND ANSWER(A) PATTERN SO THAT AUDIENCE CAN UNDERSTAND BETTER.
2. BACKGROUND
JULY 2000 C# WAS BORN. ANDERS HEJLSBERG IS THE FATHER OF THIS MODERN LANGUAGE. C# WAS BORN WITH MANY PROGRAMMING FEATURES AND WHEN ANY NEW VERSION RELEASE, IT COMES WITH NEW FEATURES. IN REAL LIFE I SEE MANY C# DEVELOPERS DID NOT UNDERSTAND FEW SEXY FEATURES PROPERLY OR NOT TO USE THOSE. IF THEY WOULD UNDERSTAND THAT FEATURES PROPERLY THEN THEY CAN CREATE MORE REUSABLE, POWERFUL, SEXY CODE AND WHICH WILL ADD MORE VALUE TO THEIR SOFTWARE PRODUCTS.
3. SEXY FEATURES
AGAIN I REPEAT, THERE ARE SO MANY PROGRAMMING FEATURES ARE AVAILABLE IN C# AND ALL THOSE ARE VERY USEFUL AND HELP US TO WRITE COMPLEX LOGIC WITH EASY CODE. EVERY DEVELOPERS SHOULD KNOW ALL OF THEM. BUT ALL FEATURES ARE NOT SEXY FEATURES. QUESTION MAY COME, BASED ON WHAT CRITERIA I SAY ONE FEATURE IS SEXY AND ANOTHER IS NOT. THERE IS NO HARD AND FIX RULE THAT I APPLIED FOR JUDGING SEXY FEATURES. IN MY DEVELOPMENT EXPERIENCE I FEEL SOME FEATURES ARE REALLY MORE ATTRACTIVE, MORE INTERESTING, HIGH APPEALING THAN OTHERS. ONLY THOSE FEATURES I AM SAYING SEXY FEATURES. I AM FOCUSING ALL THOSE FEATURES IN THIS ARTICLE. NOW I WILL EXPLAIN WITH CODE EXAMPLE ALL THOSE ONE AFTER ANOTHER.
3.1. EXTENSION METHOD
C# INTRODUCE EXTENSION METHOD FROM VERSION 3.0. IN STRAIGHT FORWARD WAY YOU CAN SAY IT IS A SPECIAL KIND OF STATIC METHOD THAT ALLOWS US TO ADD METHODS TO AN EXISTING TYPE. THE MAGIC IS IT ADDS THAT WITHOUT RECOMPILE CODE. THAT MEANS YOU CAN EXTEND ANY TYPE, IT DOES NOT MATTER WHETHER THAT TYPE'S SOURCE CODE YOU HAVE OR NOT. SO SUMMARY IS YOU CAN EXTEND BUILD IN .NET TYPES OR ANY 3RD PARTY TYPES WITHOUT THEIR SOURCE CODE MODIFICATION. IMPRESSED?
YOU NEED TO FOLLOW FOLLOWING RULE TO CREATE EXTENSION METHOD:
- METHOD SHOULD BE STATIC.
- METHOD ACCESS MODIFIER SHOULD BE PUBLIC.
- METHOD MUST BE LOCATED IN A STATIC CLASS.
- METHOD NAMESPACE MUST BE INCLUDE IN USING BLOCK WHERE IT WILL USE.
Q. I WANT TO CREATE AN EXTENSION METHOD WHICH NAME WILL BE "COUNTSPACE" AND IT WILL RETURN NO. OF SPACES AND IT SHOULD BE AVAILABLE .NET STRING OBJECT. HOW CAN I WRITE THIS?
A. SEE THE CODE BLOCK FIRST:
HIDE COPY CODE
PUBLIC STATIC CLASS EXTENSIONMETHODS
{
PUBLIC STATIC INT COUNTSPACE(THIS STRING STR)
{
IF (STRING.ISNULLOREMPTY(STR)) RETURN 0;
RETURN STR.COUNT(C => C == ' ');
}
PUBLIC STATIC STRING TOSTRING(THIS STRING STR)
{
RETURN "CODEPROJECT.COM";
}
}
[TESTMETHOD]
PUBLIC VOID COUNT_NO_OF_SPACE_IN_A_SENTENCE()
{
STRING DATA = "THIS IS A BEAUTIFUL LAPTOP.";
INT TOTALSPACE = DATA.COUNTSPACE();
ASSERT.ISTRUE(TOTALSPACE == 4);
}
YOU JUST NEED TO FOLLOW 3 STEPS:
- CREATE A STATIC CLASS NAMED EXTENSIONMETHODS
- INSIDE THIS CLASS, CREATE A STATIC METHOD NAMED COUNTSPACE WITH A PARAMETER WITH THIS KEYWORD WITH STRING TYPE. THIS WILL CREATE MAGIC FOR YOU. COUNTSPACE METHOD WILL CALCULATE THE NO OF SPACES FROM ITS ARGUMENT AND RETURN ITS VALUE.
- IF EXTENSIONMETHODS CLASS'S NAMESPACE NOT INCLUDED IN YOUR USING METHOD THEN FIRST YOU ADD THAT AND WRITE YOUR TEST CODE LIKE MY COUNT_NO_OF_SPACE_IN_A_SENTENSE.
ONE THING YOU SHOULD REMEMBER YOUR COUNTSPACE METHOD NOT ONLY YOU CAN USE WITH EXTESTION METHODBUT ALSO YOU CAN USE IT LIKE A STATIC METHOD.
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID COUNT_NO_OF_SPACE_IN_A_SENTENCE_AS_STATIC_METHOD()
{
STRING DATA = "THIS IS A BEAUTIFUL LAPTOP.";
INT TOTALSPACE = EXTENSIONMETHODS.COUNTSPACE(DATA);
ASSERT.ISTRUE(TOTALSPACE == 4);
}
BUT POINT TO BE NOTED THAT THIS IS NOT RECOMMENDED APPROACH TO USE.
3.1.1. SHOULD KNOW BEFORE USE:
- EXTENSION METHOD SCOPE IS NAMESPACE LEVEL.
- EXTENSION METHOD OVERRIDE IS NOT SUPPORTED.
- EXTENSION METHOD CAN USE INTERFACE LEVEL.
- IF OBJECT INSTANCE METHOD AND EXTENSION METHOD ARE SAME INCLUDING SIGNATURE THEN INSTANCE METHOD GET PRIORITY. FOR THAT REASON NEED TO USE EXTENSION METHOD CAREFULLY.
- IF YOU WRITE EXTENSION METHOD FOR A TYPE THAT'S SOURCE CODE YOU ARE NOT MAINTAINING THEN ALWAYS HAVE A CHANCE TO BREAK EXTENSION METHOD.
3.2. ANONYMOUS TYPE
C# INTRODUCE ANONYMOUS TYPE FROM VERSION 3.0, IT IS A SPECIAL CLASS, COMPILER CREATES IT AT RUN TIME. R THAT REASON YOU CAN CALLED IT COMPILER GENERATED CLASS TOO. ANONYMOUS TYPE HELPS TO ENCAPSULATE PUBLIC READ ONLY PROPERTY WITHOUT EXPLICITLY DEFINED TYPE. IN MANY SCENARIOS EXTRA CLASS CREATION MAY SEEMS TO BE OVERHEAD SPECIALLY WHEN YOU KNOW CLASS WILL USE ONLY ONE TIME AND NO CHANCE TO RE USE IT IN FUTURE OR NO MANAGEABILITY BENEFIT COMES . IN THOSE SCENARIOS ANONYMOUS TYPE WILL VERY USEFUL.
THE MAIN CHARACTERISTICS OF ANONYMOUS TYPES ARE AS FOLLOWS:
- ANONYMOUS TYPE SHOULD NOT BE CHILD OF OTHER TYPE. IT IMPLICITLY DERIVED FROM BUILT IN OBJECT TYPE.
- IT SHOULD NOT BE PARENT OF ANOTHER TYPE. THAT MEANS IT SHOULD NOT BE EXTENDABLE.
- COMPILER PROVIDE ANONYMOUS TYPE NAME AND DEVELOPER HAVE NO CONTROL ON IT.
- ONLY PUBLIC PROPERTY ARE ALLOWED TO BE MEMBER OF ANONYMOUS TYPE.
- NEED TO USE VAR OR DYNAMIC KEYWORD FOR TAKE REFERENCE OF ANONYMOUS TYPE OBJECT.
Q. I WANT TO CREATE ANONYMOUS TYPE. HOW CAN I CREATE THIS?
A.
TO CREATE ANONYMOUS TYPE YOU JUST USE "NEW" CONSTRUCTOR METHOD IS AS FOLLOWS:
HIDE COPY CODE
VAR OBJ = NEW {ID=1, NAME="MR. BILL"};
INSTEAD OF VAR YOU CAN USE DYNAMIC AS REPLACE OF VAR KEYWORD.
HIDE COPY CODE
DYNAMIC OBJ = NEW{ID=1, NAME="MR. BILL"};
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID CHECK_ANONYMOUS_TYPE()
{
VAR OBJ = NEW { ID = 1, NAME = "MR. BILL" };
ASSERT.ISTRUE(OBJ.ID == 1);
ASSERT.ISTRUE(OBJ.NAME.EQUALS("MR. BILL"));
}
Q. IS IT POSSIBLE TO CREATE ANONYMOUS TYPE AS ARGUMENT OR RETURN TYPE OF A METHOD?
A. YES IT IS POSSIBLE TO USE ARGUMENT OR RETURN TYPE OF CREATED ANONYMOUS TYPE. IN BOTH METHOD ARGUMENT OR RETURN TYPE, YOU NEED TO DEFIND TYPE SHOULD BE:
- OBJECT OR
- DYNAMIC
IF YOU USE OBJECT AS TYPE, YOU NEED TO USE REFLACTION FOR RETRIVE PROPERTY VALUES. SO USE DYNAMIC IS SMART APPROACH IN THIS CASE.
THE FOLLOWING CODE SAMPLE WILL SHOW HOW TO USE DYNAMIC TYPE WITH ANONYMOUS TYPE AS A METHOD ARGUMENT AND ALSO RETURN TYPE.
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID CHECK_ANONYMOUS_TYPE_AS_ARGUMENT_AS_RETURNTYPE()
{
VAR OBJ = NEW { ID = 1, NAME = "MR. BILL" };
VAR RESULT = PROCESSANONYMOUSTYPE(OBJ);
ASSERT.ISTRUE(RESULT.CODE == "101");
}
PRIVATE DYNAMIC PROCESSANONYMOUSTYPE(DYNAMIC EMPLOYEE)
{
INT ID = EMPLOYEE.ID;
STRING NAME = EMPLOYEE.NAME;
VAR NEWEMPLOYEE = NEW { CODE = (ID + 100).TOSTRING(), NAME = NAME };
RETURN NEWEMPLOYEE;
}
Q. WHAT ARE THE SCENARIOS ANONYMOUS TYPE MIGHT BE USE?
A. IF YOU WANT TO CREATE A SUB TYPE (ACTUALLY IT IS ALSO NEW TYPE) FROM ANOTHER TYPE THEN WITHOUT CREATING EXPLICIT TYPE, YOU CAN CREATE ANONYMOUS TYPE. SPECIALLY YOU WILL FIND THAT MANY LINQ EXPRESSION WHERE MAIN OBJECT CONTAIN LOTS OF PROPERTY BUT YOU NEED FEW OF THEM FOR WORKING. IN THIS CASE YOU CAN USE ANONYMOUS TYPE.
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID CREATE_ANONYMOUS_TYPE_USING_LINQ()
{
VAR EMPLIST = NEW LIST<EMPLOYEE>();
EMPLIST.ADD(NEW EMPLOYEE { ID = 1, NAME = "MR. A" });
EMPLIST.ADD(NEW EMPLOYEE { ID = 2, NAME = "MR. B" });
EMPLIST.ADD(NEW EMPLOYEE { ID = 3, NAME = "MR. C" });
VAR PEOPLE = EMPLIST.SELECT(E => NEW { E.ID, E.NAME });
FOREACH (VAR PERSON IN PEOPLE)
{
INT ID = PERSON.ID;
STRING NAME = PERSON.NAME;
}
ASSERT.ISTRUE(PEOPLE.COUNT() == 3 && PEOPLE.FIRST().ID == 1);
}
PRIVATE CLASS EMPLOYEE
{
PUBLIC INT ID { GET; SET; }
PUBLIC STRING NAME { GET; SET; }
PUBLIC INT AGE { GET; SET; }
}
HERE I CREATED PEOPLE LIST, WHICH IS CREATED FROM EXISTING EMPLIST (LIST OF EMPLOYEE). ACTAULLY PEOPLE IS A LIST OF ANONYMOUS TYPE OBJECT WHICH HAS ONLY 2 PROPERTIES ID AND NAME. THIS PROPERTIES CREATED FROM EMPLOYEE OBJECT ID AND NAME PROPERTY. IT IS ALSO POSSIBLE TO CREATE ADDITIONAL PROPERTY THAT ARE NOT EXISTS IN EMPLOYEE CLASS. THESE ARE THE POWER OF ANONYMOUS TYPE.
3.3. DELEGATE
DELEGATE FEATURE IS FIRST INTRODUCED WITH C# 1.0
Q. WHAT IS DELEGATE?
A. A SHORT DEFINITION: “A DELEGATE IS A SPECIAL KIND OF REFERENCE TYPE THAT DEFINES A METHOD SIGNATURE INSTEAD OF AN OBJECT”. THOUGH DELEGATE IS A TYPE SO YOU CAN CREATE OBJECT FROM THIS TYPE AND ASSOCIATE COMPATIBLE METHOD WITH THIS OBJECT. A DELEGATE ACTUALLY REPRESENTS A FUNCTION, THAT'S WHY PEOPLE OFTEN CALL IT A FUNCTION POINTER.
Q. CAN YOU EXPLAIN LITTLE MORE WITH CODE SAMPLE?
A. SEE THE FOLLOWING CODE BLOCK:
HIDE COPY CODE
PUBLIC DELEGATE STRING GETEMPLOYEENAMEDELEGATE(INT EMPLOYEEID);
[TESTMETHOD]
PUBLIC VOID NAMED_DELEGATE_TEST()
{
VAR EMPLOYEE = NEW GETEMPLOYEENAMEDELEGATE(GETFULLTIMEEMPLOYEENAME);
/*
ANOTHER WAY YOU CAN CALL AS FOLLOWS.
GETEMPLOYEENAMEDELEGATE EMPLOYEE = GETFULLTIMEEMPLOYEENAME;
FRAMEWORK WILL CREATE EMPLOYEE DELEGATE OBJECT AND BIND IT
WITH GETFULLTIMEEMPLOYEENAME METHOD
*/
STRING EMPLOYEENAME = EMPLOYEE(1);
ASSERT.ISTRUE(EMPLOYEENAME.EQUALS("FULLTIMEEMPLOYEE-1"));
EMPLOYEE = NEW GETEMPLOYEENAMEDELEGATE(GETPARTTIMEEMPLOYEENAME);
EMPLOYEENAME = EMPLOYEE(1);
ASSERT.ISTRUE(EMPLOYEENAME.EQUALS("PARTTIMEEMPLOYEE-1"));
}
PUBLIC STRING GETFULLTIMEEMPLOYEENAME(INT EMPID)
{
RETURN STRING.CONCAT("FULLTIMEEMPLOYEE-", EMPID);
}
PUBLIC STRING GETPARTTIMEEMPLOYEENAME(INT EMPID)
{
RETURN STRING.CONCAT("PARTTIMEEMPLOYEE-", EMPID);
}
GETEMPLOYEENAMEDELEGATE IS A DELEGATE TYPE WHICH REPRESENT A METHOD SIGNATURE WHICH SIGNATURE SHOULD CONTAINS
- 1 INT (INTEGER) TYPE PARAMETER
- STRING RETURN TYPE
GETFULLTIMEEMPLOYEENAME AND GETPARTTIMEEMPLOYEENAME ARE TWO METHODS WHICH ARE MATCHED WITH THIS GETEMPLOYEENAMEDELEGATE DELEGATE. WE CAN BIND THOSE TWO METHODS WITH THIS DELEGATE AND EXECUTE METHOD VIA DELEGATE. AFTER ANALYZING THE CODE BLOCK YOU WILL UNDERSTAND THAT DELEGATE IS ANOTHER TYPE OF FEATURE WHICH SUPPORTS POPULAR OOP FEATURE NAMED POLYMORPHISM (RUN-TIME).
ANOTHER WAY TO USE DELEGATE:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID ANONYMOUS_DELEGATE_TEST()
{
GETEMPLOYEENAMEDELEGATE GETEMPLOYEENAME = DELEGATE(INT X)
{
RETURN STRING.CONCAT("EMPLOYEE", X);
};
STRING EMPLOYEENAME = GETEMPLOYEENAME(1);
ASSERT.ISTRUE(EMPLOYEENAME.EQUALS("EMPLOYEE1"));
}
YOU CAN SEE ABOVE CODE BLOCK THAT BINDING IN-LINE CODE BLOCK TO GETEMPLOYEENAMEDELEGATE TYPE AND EXECUTE THAT CODE BLOCK VIA DELEGATE. THIS IS DIFFERENT WAY TO USE DELEGATE AND IT IS DIFFERENT FROM PREVIOUS ONE. PREVIOUSLY WE DIRECTLY BIND A METHOD WITH DELEGATE AND ABOVE CODE BLOCK WE DID NOT BIND WITH ANY METHOD, INSTEAD BIND INLINE CODE BLOCK. THIS INLINE METHOD BINDING IS CALLED ANONYMOUS METHOD. HERE WE CAN SAY THAT WE FIND TWO TYPES OF DELEGATE
- NAMED DELEGATE, WHICH IS DIRECTLY BIND WITH ANY METHOD.
- ANONYMOUS METHOD/DELEGATE WHICH IS BIND WITH IN LINE CODE BLOCK.
IN REAL LIFE, WE MAY FIND MANY SCENARIOS WHERE EXTRA METHOD CREATION SEEMS TO BE OVERHEAD. SPECIALLY YOU FIND THAT SOME CODE BLOCK WILL USE ONLY ONE TIME AND YOU WILL SURE ENOUGH THAT THESE CODE BLOCKS WILL NEVER BE RE-USE OR NO NEED TO CREATE FUNCTIONAL DECOMPOSITION. IN THESE CASES EXTRA METHOD CREATION SEEMS TO BE OVERHEAD AND WE CAN USE ANONYMOUS METHOD THERE.
Q. I WANT TO KNOW WHEN ANONYMOUS METHOD CONVERTED TO DELEGATE?
A. WELL, IT IS VERY IMPORTANT TO KNOW EACH DEVELOPERS. ACTUALLY AT COMPILE TIME ANONYMOUS METHODS ARE CONVERTED TO DELEGATE TYPE.
3.3.1. CALLBACK:
BY DEFINITION WHEN CODE BLOCK SEND TO ANOTHER METHOD AS ARGUMENT FOR EXECUTION IS CALLED CALLBACK. EXECUTION OF CALLBACK HAPPENED TWO WAYS:
- SYNCHRONOUS WAY
- ASYNCHRONOUS WAY
CODE BLOCK MIGHT BE A METHOD OR INLINE CODE BLOCK. WE CAN NOT DIRECTLY SEND A METHOD TO ANOTHER METHOD AS ARGUMENT. WE NEED TO TAKE DELEGATE'S HELP FOR SENDING CODE BLOCK/METHOD TO ANOTHER METHOD.
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID METHOD_AS_METHOD_ARGUMENT_TEST()
{
GETEMPLOYEENAMEDELEGATE GETEMPLOYEENAME = GETFULLTIMEEMPLOYEENAME;
STRING EMPLOYEENAME = PROCESSEMPLOYEENAME(GETEMPLOYEENAME);
ASSERT.ISTRUE(EMPLOYEENAME.EQUALS("FULL NAME IS: FULLTIMEEMPLOYEE-1"));
}
PUBLIC STRING PROCESSEMPLOYEENAME(GETEMPLOYEENAMEDELEGATE EMPLOYEENAMEDELEGATE)
{
STRING NAME = EMPLOYEENAMEDELEGATE(1);
RETURN STRING.CONCAT("FULL NAME IS: ", NAME);
}
HERE I AM SENDING GETEMPLOYEENAME DELEGATE TYPE OBJECT WHICH POINTS GETFULLTIMEEMPLOYEENAME METHOD AS ANOTHER PROCESSEMPLOYEENAME METHOD ARGUMENT. THIS PROCESSEMPLOYEENAME METHOD THEN EXECUTE GETFULLTIMEEMPLOYEENAME VIA DELEGATE AND RECEIVE THE RESULT AND PROCESS IT AND RETURN FINAL RESULT TO ITS CALLER.
3.3.2. MULTI-CAST DELEGATE:
WE ALREADY SAW THAT ONE METHOD IS BIND WITH ONE DELEGATE. MEANS WE SAW ONE TO ONE RELATION BETWEEN DELEGATE AND METHOD. WHEN DELEGATE IS EXECUTED UNDER THE HOOD THE BIND METHOD IS EXECUTED. BUT 1 DELEGATE CAN BIND WITH MULTIPLE METHODS TOO. MEANS
1 DELEGATE = N METHODS
IF THAT IS HAPPENED FOR METHOD BINDINGS, WE CAN SAY THAT IS MULTI-CAST DELEGATE. JUST POINT TO BE REMEMBER THAT EVERY METHOD SHOULD HAVE SAME SIGNATURE.
Q. HOW TO CREATE MULTI-CAST DELEGATE?
A. C# HAS 2 OPERATORS BY WHICH METHODS ARE BIND AND UNBIND WITH MULTI-CAST DELEGATE. THESE ARE:
- +=
- -=
+= OPERATOR IS USED FOR BINDING METHOD AND -= IS USED FOR UN-BIND METHOD.
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID MULTICAST_DELEGATE_TEST()
{
GETEMPLOYEENAMEDELEGATE MULTICASTDELEGATEOBJECT = GETFULLTIMEEMPLOYEENAME;
//BIND ANOTHER NEW METHOD
MULTICASTDELEGATEOBJECT += GETPARTTIMEEMPLOYEENAME;
//IT WILL RETURN 2ND METHOD STRING BUT EXECUTE BOTH METHODS
STRING EMPLOYEENAMES = MULTICASTDELEGATEOBJECT(1);
EMPLOYEENAMES = STRING.EMPTY;
IENUMERABLE<DELEGATE> DELEGATELIST = MULTICASTDELEGATEOBJECT.GETINVOCATIONLIST();
FOREACH (DELEGATE DELEGATEOBJECT IN DELEGATELIST)
{
VAR DEL = DELEGATEOBJECT AS GETEMPLOYEENAMEDELEGATE;
EMPLOYEENAMES += DEL(1);
}
ASSERT.ISTRUE("FULLTIMEEMPLOYEE-1PARTTIMEEMPLOYEE-1" == EMPLOYEENAMES);
//UN-BIND LAST METHOD
MULTICASTDELEGATEOBJECT -= GETPARTTIMEEMPLOYEENAME;
EMPLOYEENAMES = STRING.EMPTY;
FOREACH (DELEGATE DELEGATEOBJECT IN MULTICASTDELEGATEOBJECT.GETINVOCATIONLIST())
{
VAR DEL = DELEGATEOBJECT AS GETEMPLOYEENAMEDELEGATE;
EMPLOYEENAMES += DEL(1);
}
ASSERT.ISTRUE("FULLTIMEEMPLOYEE-1" == EMPLOYEENAMES);
}
Q. HOW CAN I CALL MULTI-CAST DELEGATE?
A. YOU CAN CALL MULTI-CAST DELEGATE SAME WAY AS YOU CALL SIMPLE DELEGATE.
HIDE COPY CODE
DELEGATEOBJECT(); OR DELEGATEOBJECT.INVOKE(); //SYNCHORNOUS CALL
BUT PROBLEM WILL RAISE WHEN DELEGATEOBJECT WILL RETURN VALUES AND YOU NEED TO CAPTURE THOSE VALUES. IF YOU SIMPLY EXECUTE:
HIDE COPY CODE
STRING RESULT = DELEGATEOBJECT();
IF DELEGATEOBJECT BIND WITH 3 DIFFERENT METHODS AND EACH OF THEM RETURNS STRING DATA THEN RESULT VARIABLE WILL STORE ONLY LAST METHOD'S RETURN VALUE.
Q. WHAT WILL BE SOLUTION?
A. FOR CAPTURE ALL RETURN VALUES, YOU NEED TO EXECUTE DELEGATE WITH DIFFERENT WAYS:
- YOU NEED TO RETRIEVE ALL DELEGATES FROM THE DELEGATE INVOCATION LIST.
- CAST IT AS ITS DESIRED TYPE
- EXECUTE EACH DELEGATE
- STORE EACH RETURN VALUE INDIVIDUALLY OR CONCAT ALL IN A ONE.
HIDE COPY CODE
STRING EMPLOYEENAMES = STRING.EMPTY;
IENUMERABLE<DELEGATE> DELEGATELIST = MULTICASTDELEGATEOBJECT.GETINVOCATIONLIST();
FOREACH (DELEGATE DELEGATEOBJECT IN DELEGATELIST)
{
VAR DEL = DELEGATEOBJECT AS GETEMPLOYEENAMEDELEGATE;
EMPLOYEENAMES += DEL(1);
}
ASSERT.ISTRUE("FULLTIMEEMPLOYEE-1PARTTIMEEMPLOYEE-1" == EMPLOYEENAMES);
3.3.3. DELEGATE FOR ASYNCHRONOUS METHOD EXECUTION:
SUPPOSE YOU HAVE A LONG RUNNING METHOD WHICH YOU ARE CURRENTLY EXECUTING SYNCHRONOUSLY. NOW YOU DECIDE FOR BETTER PERFORMANCE YOU WILL EXECUTE THIS METHOD ASYNCHRONOUSLY. HOW CAN YOU DO THIS WITH VERY SIMPLE WAY? IN THIS SCENARIO DELEGATE IS YOUR FRIEND. DELEGATE OBJECT HAS A METHOD NAMEDBEGININVOKE. IT WILL HELP TO EXECUTE ANY SYNCHRONOUS METHOD ASYNCHRONOUSLY.
THERE ARE FEW WAYS TO CALL BEGININVOKE() METHOD FOR ASYNCHRONOUS CALLING. I SHOW HERE A VERY SIMPLE WAY:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID ASYNCMETHOD_USING_DELEGATE()
{
ACTION AC = SYNCHRONOUS_METHOD_WILL_CALL_ASYNCHRONOUSLY;
IASYNCRESULT ASYCNRESULT = AC.BEGININVOKE(NULL, NULL);
//YOUR NEXT CODE BLOCK RUN PARALLY...
AC.ENDINVOKE(ASYCNRESULT);
}
PUBLIC VOID SYNCHRONOUS_METHOD_WILL_CALL_ASYNCHRONOUSLY()
{
THREAD.SLEEP(15000);
}
3.3.4. DELEGATE VS INTERFACE:
DELEGATE AND INTERFACE ARE TWO DISTINCT CONCEPT OF C# BUT BOTH HAVE COMMON FEATURES:
- BOTH CONTAIN ONLY DECLARATION TYPE.
- DIFFERENT METHOD/CLASS CAN IMPLEMENT THEM.
- BOTH SUPPORT RUN-TIME POLYMORPHISM.
FOR THOSE COMMON FEATURES IN REAL LIFE, YOU CAN FIND SAME OR SIMILAR PROBLEM CAN SOLVE WITH BOTHDELEGATE AND INTERFACE.
Q. CAN YOU EXPLAIN IT WITH CODE EXAMPLE?
A. YES CERTAINLY. STRATEGY DESIGN PATTERN IS AN EXAMPLE. STRATEGY PATTERN SOLVED PROBLEM LIKE WHERE FUNCTION OUTPUT IS SAME BUT WAYS OF COMPLETE THE FUNCTION IS DIFFERENT AND RUN-TIME/DYNAMICALLY WAYS MIGHT BE SWITCH. STRATEGY PATTERN IS IMPLEMENTED BY INTERFACE. DECLARE AN INTERFACE AND WRITE 2 OR 3 IMPLEMENTATION CLASSES OF THIS INTERFACE. RUN TIME YOU SHOULD DEFINE WHICH IMPLEMENTATION YOU WILL USE TO PRODUCE DESIRE OUTPUT. IN THIS IMPLEMENTATION YOU CAN REPLACE INTERFACE WITH DELEGATE AND GET SAME OUTPUT. SIMILAR LIKE SCENARIO YOU MAY GET IN REAL LIFE.
Q. WHEN TO USE INTERFACE WHEN TO DELEGATE?
A. THERE IS NO CLEAR AND STRAIGHT FORWARD RULE FOR SELECTING INTERFACE OR DELEGATE. THE FOLLOWING ARE REPRESENT SOME POINT WHICH MAY USE AS GUIDE LINE.
DELEGATE | INTERFACE |
ENCAPSULATE STATIC METHOD. | GROUPING RELATED METHODS. |
A CLASS MAY NEED MORE THAN ONE IMPLEMENTATION OF A METHOD. | NEED TO CAST FROM ONE CLASS TO ANOTHER. |
CALLER HAVE INTERESTED ONLY FOR A PARTICULAR METHOD. | CLASS NEEDS ONLY IMPLEMENTATION OF METHODS. |
PUBLISHER-SUBSCRIBER PATTERN IMPLEMENTATION. |
AT THE END IF WE SUMMARIZE DELEGATE FEATURES WE CAN PIN THE FOLLOWING POINTS:
- DELEGATE IS TYPE-SAFE METHOD POINTER.
- DELEGATE ALLOWS US TO SEND CODE-BLOCK FROM ONE METHOD TO ANOTHER.
- DELEGATE SUPPORTS RUN-TIME POLIMORPHISM.
- DELEGATE CAN USE AS A CALLBACK METHOD.
- DELEGATE CAN USE AS A CHAIN I.E. MULTI-CAST DELEGATE.
- DELEGATE CAN CREATE ONE METHOD (ANONYMOUS METHOD) INSIDE ANOTHER METHOD.
- SOME SCENARIOS DELEGATE CAN USE IN REPLACE OF INTERFACE.
3.4. LAMBDA EXPRESSION
LAMBDA EXPRESSION IS A SPECIAL FORM OF ANONYMOUS FUNCTION. AFTER USING LAMBDA, WE CAN CREATE EITHER
- DELEGATE OR
- EXPRESSION TREE
AFTER CREATING DELEGATE WE CAN EXECUTE CODE DIRECTLY. BUT IF WE CREATE EXPRESSION TREE WE NEED TO DO ONE EXTRA STEP. THAT IS FIRST COMPILED EXPRESSION TREE, AFTER COMPILATION DONE IT WILL CREATE DELEGATE AND THIS DELEGATE RUN CODE.
Q. WHY ONE EXTRA STEP IS NEEDED FOR EXPRESSION TREE?
A. EXPRESSION TREE IS NOT CODE BLOCK RATHER IT IS ACTUALLY DATA BLOCK. THOUGH IT IS DATA BLOCK SO DIRECTLY EXECUTE IS NOT POSSIBLE. FOR THAT REASON COMPILATION IS NEEDED, AFTER COMPILATION USING COMPILE() METHOD IT CREATES DELEGATE WHICH IS EXECUTABLE.
Q. CAN YOU EXPLAIN MORE WITH CODE SAMPLE?
A. PLEASE SEE THE FOLLOWING CODE BLOCKS:
3.4.1. DELEGATE CREATION:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID USE_LAMBDA_CREATE_DELEGATE()
{
CALCULATIONDELEGATE ADDITION = (X, Y) => X + Y;
INT RESULT = ADDITION(2, 3);
ASSERT.ISTRUE(RESULT == 5);
}
3.4.2. EXPRESSION TREE CREATION:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID USE_LAMBDA_CREATE_EXPRESSIONTREE()
{
EXPRESSION<CALCULATIONDELEGATE> ADDITIONEXPRESSIONTREE = (X, Y) => X + Y;
CALCULATIONDELEGATE ADDITIONDELEGATE = ADDITIONEXPRESSIONTREE.COMPILE();
INT RESULT = ADDITIONDELEGATE(3, 2);
ASSERT.ISTRUE(RESULT == 5);
}
3.4.3. LAMBDA TYPES:
THERE ARE 2 TYPES OF LAMBDA:
- EXPRESSION LAMBDA
- STATEMENT LAMBDA
1. EXPRESSION LAMBDA: LAMBDA USE AN OPERATOR CALLED LAMBDA OPERATOR. IT LOOKS => AND PRONOUNCED "SUCH THAT"
HIDE COPY CODE
(INPUT PARAMETERS) => EXPRESSION;
- LEFT SIDE OF => IS CALLED PARAMETERS.
- RIGHT SIDE OF => IS CALLED EXPRESSIONS.
CODE EXAMPLE:
LAMBDA EXPRESSION WITH 2 INPUT PARAMETERS:
HIDE COPY CODE
PRIVATE DELEGATE BOOL EQUALITYCHECKDELEGATE(INT X, INT Y);
[TESTMETHOD]
PUBLIC VOID EXPRESSION_LAMDA()
{
EQUALITYCHECKDELEGATE DELEGATEOBJECT = (X, Y) => X == Y;
BOOL MATCHED = DELEGATEOBJECT.INVOKE(2, 2);
ASSERT.ISTRUE(MATCHED);
}
() EMPTY PARENTHASIS IS ALSO USED IF NO PARAMETER IS DEFINED:
HIDE COPY CODE
PRIVATE DELEGATE BOOL EQUALITYCHECKDELEGATENOPARAM();
[TESTMETHOD]
PUBLIC VOID EXPRESSION_LAMDA_NO_PARAM()
{
EQUALITYCHECKDELEGATENOPARAM DELEGATEOBJECT = () => 2 == 2;
BOOL MATCHED = DELEGATEOBJECT.INVOKE();
ASSERT.ISTRUE(MATCHED);
}
2. STATEMENT LAMBDA: STATEMENT LAMBDA IS SIMILAR TO EXPRESSION LAMBDA BUT STARTED WITH CURLY BRACKET AND IT DEFIES ITS BLOCK. IF DELEGATE HAVE RETURN TYPE THEN MANUALLY NEED TO RETURN VALUE INSIDE LAMBDA STATEMENT SIMILAR WAY WE USE IN METHOD.
HIDE COPY CODE
(INPUT PARAMETERS) => { STATEMENT(S); };
EXAMPLE:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID STATEMENT_LAMDA()
{
EQUALITYCHECKDELEGATE DELEGATEOBJECT = (X, Y) =>
{
IF (X == Y)
RETURN TRUE;
ELSE
RETURN FALSE;
};
BOOL MATCHED = DELEGATEOBJECT.INVOKE(5, 5);
ASSERT.ISTRUE(MATCHED);
}
3.4.4. ASYNC LAMBDA:
IF WE CREATE LAMBDA EXPRESSION OR STATEMENT WITH ASYNCHRONOUS PROCESSING WE CAN CALL THAT ASYNCLAMBDA. IT ACTUALLY A VERY SIMPLE WAY TO CREATE ASYNCHRONOUS DELEGATE.
FOR ASYNCHRONOUS PROCESSING WE NEED TO TOOLS:
- ASYNC MODIFIER
- AWAIT STATEMENT
EXAMPLE:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID ASYNC_LAMBDA()
{
FUNC<TASK> TASK = ASYNC () =>
{
AWAIT TASK.DELAY(5000);
};
//YOUR ANOTHER LOGIC CODE...
TASK().WAIT();
}
I WILL EXPLAIN ASYNC AND AWAIT IN DETAILS IN ANOTHER SECTION OF THIS ARTICLE.
3.4.5. TYPE INFERENCE:
IF YOU ANALYZE THE CODE, YOU WILL SEE THAT WE DO NOT DEFINE PARAMETER TYPE IN LAMBDA PARAMETER SECTION.
Q. HOW COMPILER KNOWS THE PARAMETER TYPE?
A. BASED ON LAMBDA BODY, COMPILER INFER PARAMETER TYPES. IF YOU SEE THE LINQ API
HIDE COPY CODE
IENUMERABLE<EMPLOYEE> EMPLOYEELIST = GETEMPLOYEES();
IENUMERABLE<EMPLOYEE> NEWEMPLOYEELIST = EMPLOYEELIST.WHERE(E => E.SALARY > 10000);
HERE INPUT PARAMETER E IS INFERRED BY COMPILER AS EMPLOYEE TYPE OBJECT AND IN LAMBDA BODY YOU CAN USE ANY PUBLIC MEMBER OF EMPLOYEE OBJECT. ONE IMPORTANT POINT SHOULD BE REMEMBER THAT LAMBDA EXPRESSION THEMSELVES DO NOT HAVE A TYPE.
3.4.6. LAMBDA EXPRESSION RULES:
LAMBDA EXPRESSION FOLLOWS SOME GENERAL RULES THAT WE SHOULD KNOW:
- NO. OF LAMBDA PARAMETERS SHOULD BE EQUAL TO ITS DELEGATE PARAMETERS. N LAMBDA PARAMETERS = N DELEGATE PARAMETERS;
- EACH LAMBDA PARAMETER MUST IMPLICITLY CONVERTIBLE WITH ITS CORRESPONDING DELEGATE PARAMETER.
- IF LAMBDA RETURN ANY VALUE THEN VALUE TYPE MUST BE IMPLICITLY CONVERTIBLE WITH ITS DELEGATE RETURN TYPE.
3.4.7. VARIABLE SCOPE:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID LAMBDA_VARIABLE_SCOPE()
{
VAR SALARY = NEW SALARY();
INT NEWSALARY = SALARY.ADDANDGETSALARY(100);
ASSERT.ISTRUE(NEWSALARY == 100);
INT NEWSALARY2 = SALARY.INCREASESALARY(100);
ASSERT.ISTRUE(NEWSALARY2 == 200);
}
PRIVATE CLASS SALARY
{
PUBLIC FUNC<INT,INT> INCREASESALARY = NULL;
PUBLIC INT ADDANDGETSALARY(INT ADDEDAMOUNT)
{
INT SALARYAMOUNT = 0;
SALARYAMOUNT += ADDEDAMOUNT;
INCREASESALARY = (INT ADDEDNEWAMOUNT) =>
{
SALARYAMOUNT += ADDEDNEWAMOUNT;
RETURN SALARYAMOUNT;
};
RETURN SALARYAMOUNT;
}
}
IF YOU ANALYSIS THE ABOVE CODE YOU WILL FIND THAT LOCAL VARIABLE SALARYAMOUNT, ITS SCOPE DEFINE IN METHOD ADDANDGETSALARY METHOD. THAT MEANS IF ADDANDGETSALARY METHOD EXECUTION COMPLETE THEN LOCAL VARIABLE SALARYAMOUNT WILL AUTOMATICALLY REMOVE FROM MEMORY BY THE HELP OF GARBAGE COLLECTOR. BUT HERE INSIDE THE LAMBDA STATEMENT THIS LOCAL VARIABLE IS USE AS ITS OUTER VARIABLE REFERENCE AND INCREASE ITS VALUE.
WHEN I DIRECTLY CALLED
HIDE COPY CODE
SALARY.ADDANDGETSALARY(100);
IT RETURNS 100.
BUT AFTER THAT WHEN I EXECUTE
HIDE COPY CODE
SALARY.INCREASESALARY(100);
IT RETURNS 200. MEANS IT STORED PREVIOUS 100. THAT IS THE INTERESTING PART. THOUGH DESTROYED SALARYAMOUNT VARIABLE SCOPE, STILL THE VARIABLE IS IN MEMORY.
Q. HOW IT IS POSSIBLE?
A. THE REASON IS DELEGATE STILL REFERENCING THIS VARIABLE.
Q. I SEE IT PREVIOUSLY IN JAVASCRIPT. IS IT THAT CLOSURES?
A. YES. THE CONCEPT OF THIS IS CALLED CLOSURES. NOW I AM TALKING ABOUT C# CLOSURES. ACTUALLY CLOSURES IS LANGUAGE INDEPENDENT CONCEPT. IN A SHORT WE CAN DEFINE CLOSURES: INNER SCOPE/METHOD TAKES REFERENCE OF ITS OUTER SCOPE/METHOD LOCAL VARIABLE. LAMBDA USE CLOSURES TO MANAGE THAT. CLOSURES CONCEPT IS VERY IMPORTANT CONCEPT WHICH SHOULD BE CLEAR TO ALL DEVELOPERS SPECIALLY WHO ARE WORKING WITH LAMBDA EXPRESSION.
3.5. ASYNC-AWAIT PAIR
ASYNC AND AWAIR ARE ACTUALLY 2 DIFFERENT CODE MARKERS. WHAT THEY MARK? THEY MARK THE CODE POSITION WHERE THE CODE NEED TO RESTART AFTER TASK IS COMPLETED. ASYC USE WITH A METHOD LIKE A MODIFIER WHICH INDICATES INSIDE THE METHOD BODY, AWAIT STATEMENT IS THERE, CODE STATEMENT MAY NEED TO RESTART AFTER TASK COMPLETION.
MICROSOFT INTRODUCED TASK BASED ASYNCHRONOUS PATTERN ( TAP ) FROM .NET VERSION 4. THIS TAP IS USED BASED ON 2 TYPES:
- TASK
- TASK<TRESULT>
TASK OBJECT REPRESENT EXECUTING CODE THAT WILL PROVIDE RESULT IN FUTURE. TASK OBJECT HAS SOME FEATURES:
- TASK SCHEDULING
- ESTABLISH RELATIONSHIP BETWEEN PARENT AND CHILD TASK.
- SUPPORT COOPERATIVE CANCELLATION
- WITHOUT EXTERNAL WAIT HANDLES, WAIT CAN BE SIGNALED.
- ATTACH TASK CONTINUOUSLY.
MICROSOFT RECOMMENDED FOR CURRENT DEVELOPMENT, TAP IS THE BEST APPROACH FOR ASYNCHRONOUS PROGRAMMING.
C# INTRODUCED ASYNC AND AWAIT PAIR C# VERSION 5 AND IT IS USED WITH TAP.
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID ASYNC_AWAIT()
{
VAR LIST = NEW LIST<TASK>();
FOR (INT I = 0; I < 3; I++)
{
TASK T = WRITEFILEASYNC(I);
LIST.ADD(T);
}
TASK.WAITALL(LIST.TOARRAY());
}
PRIVATE ASYNC TASK WRITEFILEASYNC(INT X)
{
AWAIT TASK.RUN(NEW ACTION(WRITEFILE));
}
PRIVATE READONLY OBJECT _LOCKER = NEW OBJECT();
PRIVATE VOID WRITEFILE()
{
THREAD.SLEEP(4000);
LOCK (_LOCKER)
{
FILE.APPENDALLLINES("D:\\ABC.LOG", NEW[] { DATETIME.NOW.TOSTRING("HH:MM:SS:T") });
}
}
FOR BETTER RESPONSIVE APPLICATION DEVELOPMENT, ASYNC BASED ASYNCHRONOUS PROCESSING IS VERY USEFUL. SOME USEFUL CASES:
- FILE INPUT/OUTPUT
- DOWNLOAD DATA FROM WEB
- NETWORK STREAMING
- RESPONSIVE UI (WITHOUT BLOCKING MAIN UI THREAD)
BEFORE USE ASYNC-AWAIT PAIR FOR ASYNCHRONOUS PROGRAMMING YOU SHOULD KNOW THE FOLLOWING:
- AS A STRONG CONVENTION YOU USE ASYNC AS POST PREFIX FOR METHOD WHERE USE ASYNC MODIFIER.
- YOU SHOULD KNOW THAT COMPILER WORKS AND MANAGE EVERYTHING RELATED TO ASYNCHRONOUS PROCESSING BY STATE MACHINE WORKFLOW.
- IN ASYNC METHOD, AT LEASE ONE AWAIT SHOULD BE EXISTS OTHERWISE METHOD WILL WORK SYNCHRONOUSLY.
3.5.1. EXCEPTION HANDLING:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID EXCEPTIONHANDLING_WITH_ASYNC_WRONG_WAY()
{
VAR TID = THREAD.CURRENTTHREAD.MANAGEDTHREADID;
BOOL EXCEPTIONCATCHED = FALSE;
TASK T = NULL;
TRY
{
T = LONGPROCESSASYNC();
}
CATCH(EXCEPTION EX)
{
EXCEPTIONCATCHED = TRUE;
}
T.WAIT();
ASSERT.ISTRUE(EXCEPTIONCATCHED);
}
PRIVATE ASYNC TASK LONGPROCESSASYNC()
{
AWAIT TASK.RUN(NEW ACTION(LONGPROCESS));
}
PRIVATE VOID LONGPROCESS()
{
VAR TID = THREAD.CURRENTTHREAD.MANAGEDTHREADID;
THROW NEW INVALIDOPERATIONEXCEPTION("OPERATON IS INVALID. PLEASE VERIFY YOUR CODE.");
THREAD.SLEEP(5000);
}
IF YOU SEE AND RUN ABOVE CODE BLOCK, THE RESULT WILL BE TEST CASE FAILURE. WHY IT WILL FAILED? BECAUSE WE DID NOT SET TRY BLOCK IN A PROPER WAY. I INTENTIONALLY THROWING INVALIDOPERATIONEXCEPTION FROMLONGPROCESS() METHOD BUT TRY BLOCK DO NOT CATCH THIS EXCEPTION. REASON BEHIND THAT IS INSIDE 2 DIFFERENT THREAD, THE CODE IS RUNNING AND IT IS NOT POSSIBLE TO CATCH EXCEPTION WHICH IS THROWN FROM ANOTHER THREAD WITHOUT JOIN THE THREADS. (I USE HERE A VARIABLE TID BY WHICH WE WILL TEST 2 ARE DIFFERENT THREAD OR NOT.) TASK.WAIT() METHOD WILL JOIN THE THREADS SO IF WE SET TRY BLOCK FOR WAIT() METHOD THEN IT IS POSSIBLE TO CATCH EXCEPTION.
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID EXCEPTIONHANDLING_WITH_ASYNC_RIGHT_WAY()
{
VAR TID = THREAD.CURRENTTHREAD.MANAGEDTHREADID;
BOOL EXCEPTIONCATCHED = FALSE;
TASK T = LONGPROCESSASYNC();
TRY
{
T.WAIT();
}
CATCH (AGGREGATEEXCEPTION EX)
{
EXCEPTIONCATCHED = TRUE;
}
ASSERT.ISTRUE(EXCEPTIONCATCHED);
}
ANOTHER OBSERVATION! IF YOU ANALYSIS THE CODE YOU WILL FIND THAT FROM METHOD LONGPROCESS() THROWING INVALIDOPERATIONEXCEPTION BUT IN CATCH BLOCK USING AGGREGATEEXCEPTION. THE REASON BEHIND THAT WHEN ANY EXCEPTION THROWS FROM ASYNC METHOD FIRST IT WRAPS WITH AGGREGATEEXCEPTION AND THEN THROW IT. SO IF YOU NEED ACTUAL EXCEPTION YOU NEED TO EXPLORE AGGREGATEEXCEPTION.INNEREXCEPTIONPROPERTY.
3.5.2. CANCEL ASYNCHRONOUS ONGOING TASK:
QUESTION IS HOW TO CANCEL ONGOING/RUNNING TASK? CANCELLATIONTOKENSOURCE IS AN OBJECT WHICH HELPS US TO CANCEL ONGOING TASK.
Q. CAN YOU SHOW ME FEW CODE SAMPLE?
A. FOLLOWING IS THE CODE:
HIDE COPY CODE
PRIVATE CANCELLATIONTOKENSOURCE _CANCELTOKENSOURCE;
[TESTMETHOD]
PUBLIC VOID CANCEL_ONGOING_ASYNC_TASK()
{
TASK T = LONGPROCESSASYNC2();
CANCELTASK();
IF (!_CANCELTOKENSOURCE.ISCANCELLATIONREQUESTED)
{
T.WAIT();
}
}
PRIVATE VOID CANCELTASK()
{
_CANCELTOKENSOURCE.CANCEL();
}
PRIVATE ASYNC TASK LONGPROCESSASYNC2()
{
_CANCELTOKENSOURCE = NEW CANCELLATIONTOKENSOURCE();
AWAIT TASK.RUN(NEW ACTION(LONGPROCESS2));
}
PRIVATE VOID LONGPROCESS2()
{
THREAD.SLEEP(15000);
}
PREVIOUSLY IT WAS ALWAYS A CHALLENGE TO CANCEL RUNNING CODE. USING CANCELLATIONTOKENTSOURCE OBJECT ANY TIME WE CAN CANCEL ONGOING TASK WITHOUT FACING MUCH CHALLENGE. THANKS TO CANCELLATIONTOKENTSOURCE FOR ITS NICE SUPPORT!
3.6. GENERICS
HIDE COPY CODE
PUBLIC CLASS MYFIRSTGENERICCLASS<T> WHERE T : STRUCT
{
PUBLIC T GETDOUBLE(T VALUE)
{
VAR INPUT = (CONVERT.CHANGETYPE(VALUE, TYPEOF(INT)));
INT NEWA = INT.PARSE(INPUT.TOSTRING());
NEWA *= 2;
RETURN (T)(CONVERT.CHANGETYPE(NEWA, TYPEOF(T)));
}
}
C# INTRODUCED GENERICS FROM ITS VERSION 2 AND LATTER ITS FEATURE IS ENHANCED. GENERIC INTRODUCE CONCEPT OF TYPE PARAMETER. BASED ON THIS TYPE PARAMETER IT IS POSSIBLE TO CREATE NEW CLASS OR METHOD THAT CAN WORK WITH VARIOUS TYPE WITHOUT EXPLICITLY DECLARE THOSE TYPES.
YOU CAN APPLY GENERIC WITH FOLLOWING ITEMS:
- CLASS
- METHOD
- INTERFACE
- DELEGATE
- EVENT
Q. WHAT ARE THE BENEFITS WE CAN GET USING GENERICS?
A. THE FOLLOWING BENEFIT WE CAN GET USING GENERICS:
- CLEAN CODE.
- MAXIMUM CODE REUSE.
- TYPE SAFETY.
- EXTRACT GENERIC TYPE INFORMATION AT RUN TIME USING REFLECTION.
- PRODUCE HIGHER QUALITY CODE.
Q. NEED TO KNOW HOW GENERIC TYPE IS CONSTRUCTED?
A. DURING COMPILATION TIME, WHEN GENERIC TYPES OR GENERIC METHODS ARE CONVERTED TO MSIL (MICROSOFT INTERMEDIATE LANGUAGE) THAT TIME IT CONTAINS META DATA WHERE GENERIC TYPE PARAMETERS ARE DEFINED. ACTUAL CONSTRUCTION PROCESS OF GENERICS TYPE IS BASED ON PARAMETER CONSTRAINT EITHER
- VALUE TYPE OR
- REFERENCE TYPE.
VALUE TYPE AND REFERENCE TYPE GENERIC TYPE CONSTRUCTION ARE DIFFERENT.
3.6.1. GENERIC TYPE PARAMETER CONSTRAINT:
WE CAN APPLY CONSTRAINTS WHEN CREATE GENERIC TYPE. CREATING OBJECT OF THIS GENERIC TYPE, CLIENT CODE MUST RESPECT THE CONSTRAINTS OTHERWISE IT WILL PRODUCE COMPILE TIME ERROR. FOLLOWING TABLE WILL SHOW FEW CONSTRAINTS:
CONSTRAINT | DESCRIPTION |
---|---|
T: CLASS | ALL REFERENCE TYPE I.E. CLASS, INTERFACE, DELEGATE |
T: NEW() | TYPE ARGUMENT MUST HAVE A PARAMETER LESS CONSTRUCTOR. |
T: STRUCT | ANY KIND OF VALUE TYPE I.E. INT, LONG ETC |
3.6.2. MULTIPLE GENERIC TYPE PARAMETER:
NO PROBLEM FOR DEFINING MULTIPLE TYPE PARAMETERS FOR A GENERIC TYPE.
HIDE COPY CODE
PUBLIC CLASS MULTIPARAMETERSGENERICCLASS<T1, T2>
{
PUBLIC VOID PROCESS(T1 VALUE1, T2 VALUE2)
{
//CODE BLOCK...
}
}
3.6.3. GENERIC METHOD:
HIDE COPY CODE
[TESTMETHOD]
PUBLIC VOID GENERIC_METHOD_TEST()
{
INT RETURNVALUE = GENERICMETHOD<INT>(5);
ASSERT.ISTRUE(RETURNVALUE == 10);
}
PUBLIC T GENERICMETHOD<T>(T INPUTVALUE) WHERE T:STRUCT
{
VAR R =CONVERT.TOINT32(INPUTVALUE) * 2;
RETURN (T)CONVERT.CHANGETYPE(R, TYPEOF(T));
}
YOU CAN DECLARE AND USE GENERIC METHOD SAME WAY AS YOU DECLARE AND USE GENERIC CLASS. WHEN YOU CALL THE GENERIC METHOD THAT TIME YOU NEED TO PASS YOUR TYPE ARGUMENTS.
3.6.4. GENERIC COLLECTION:
THERE ARE MANY GENERIC COLLECTIONS ARE DEFINED FOR YOU IN .NET. IF YOU WANT YOU CAN WRITE YOUR OWN GENERIC COLLECTION TOO. THE FOLLOWING WILL SHOW SOME GENERIC COLLECTION LIST WHICH IS IN .NET FRAMEWORK.
- ILIST<T>
- LIST<T>
- ICOLLECTION<T>
- DICTIONARY<K, T>
- IENUMERABLE<T>
- STACK<T>
- QUEUE<T>
3.6.5. GENERIC DELEGATE:
WE CAN USE GENERICS TYPE PARAMETERS FOR DELEGATE. IN THAT CASE METHODS THAT WILL BIND WITH THIS DELEGATE MUST MATCH.
HIDE COPY CODE
PRIVATE DELEGATE T MYFIRSTGENERICDELEGAGTE<T>(T ITEM) WHERE T : STRUCT;
[TESTMETHOD]
PUBLIC VOID GENERIC_DELEGATE_TEST()
{
MYFIRSTGENERICDELEGAGTE<INT> DELOBJECT = GETDOUBLE;
INT RETURNVALUE = DELOBJECT(5);
ASSERT.ISTRUE(RETURNVALUE == 10);
}
PRIVATE INT GETDOUBLE(INT X)
{
RETURN X*2;
}
3.6.6. GENERIC REPOSITORY CODE SAMPLE:
THE FOLLOWING I SHOW GENERIC REPOSITORY PATTERN IMPLEMENTATION USING GENERICS:
HIDE SHRINK COPY CODE
PUBLIC ABSTRACT CLASS ENTITYBASE { PUBLIC LONG ID { GET; SET; } PROTECTED ENTITYBASE() { } PUBLIC ABSTRACT VOID VALIDATE(); } PUBLIC CLASS CUSTOMER : ENTITYBASE { PUBLIC STRING CUSTOMERNAME { GET; SET; } PUBLIC OVERRIDE VOID VALIDATE() { } } PUBLIC INTERFACE IREPOSITORY<T> WHERE T : ENTITYBASE { T GETBYID(LONG ID); VOID SAVEORUPDATE(T ENTITY); } PUBLIC ABSTRACT CLASS REPOSITORYBASE<T> : IREPOSITORY<T> WHERE T : ENTITYBASE { PROTECTED READONLY STRING _CONNTECTIONSTRING; PROTECTED REPOSITORYBASE() { _CONNTECTIONSTRING = CONFIGURATIONMANAGER.CONNECTIONSTRINGS["DB"].CONNECTIONSTRING; } PROTECTED REPOSITORYBASE(STRING CONNECTIONSTRING) { _CONNTECTIONSTRING = CONNECTIONSTRING; } PUBLIC ABSTRACT T GETBYID(LONG ID); PUBLIC ABSTRACT VOID SAVEORUPDATE(T ENTITY); } INTERNAL CLASS CUSTOMERREPOSITORY : REPOSITORYBASE<CUSTOMER> { PUBLIC OVERRIDE CUSTOMER GETBYID(LONG ID) { VAR PRMID = NEW SQLPARAMETER("@ID", ID); //RETURN BASE.GETBYIDINTERNAL(SP_CUSTOMER_GET_BY_ID, PRMID); RETURN NEW CUSTOMER(); } PUBLIC OVERRIDE VOID SAVEORUPDATE(CUSTOMER ENTITY) { VAR PRMID = NEW SQLPARAMETER("@ID", ENTITY.ID); VAR PRMCUSTOMERNAME = NEW SQLPARAMETER("@CUSTOMERNAME", ENTITY.CUSTOMERNAME); VAR PARAMLIST = NEW LIST<SQLPARAMETER> {PRMID, PRMCUSTOMERNAME}; //BASE.SAVEORUPDATEINTERNAL(SP_CUSTOMER_SAVE_UPDATE, PARAMLIST); } }
0 comments:
Post a Comment