During preparation for exam 70-483, I found lots of websites containing links to various manuals that helped me a lot. But what really helped me was the memo notes I composed for myself and which I’d like to share in this article. I do not target to provide a detailed description of C#, I just want to refresh your memory and to highlight certain aspects of C#. If you are not familiar with some aspects, it means you have gaps in your knowledge that should be eliminated.
Also, I will not cover testing issues, but the tricks, nuances listed below may help you.
Let’s start with simple things which, nevertheless, are often neglected, but are widely used in tests:
To declare an int variable and assign the 0 value to it at once, we can:
var myInt=0; or int myInt=0;
To declare an int variable and assign a default value to it at once, we can:
int myInt = new int();
Explicit and Implicit Typing
int i = 42; // this is explicit typing – the int variable type is specified. var i = 42; // this is implicit typing. The compiler sets a type for the variable basing on its value.
Use the implicit typing for:
- anonymous testing – var anonymousType = new { Name = «Alex» }; ar anonymousType = new { Name = «Alex» };
- getting result from the LINQ query – var queryExpression = from c in customers where c.Name == «Andrey» select c;
- using complex generic types – var searchList = new Dictionary();
- implicit typing is possible only for local variables.
Boxing/Unboxing
Boxing is the conversion of a value type into a reference type.
Unboxing is vice versa, the conversion of a reference type into a value type. Unboxing requires Cast.
Boxing/Unboxing copies the variable value.
In terms of calculation speed, boxing is an operation taking up a considerably large amount of processor resources.
A simple example:
int i = 123; // The following string boxes the i variable (boxing takes place) object o = i; // object – is the reference type, and int is the value type. j = (int)o; // unboxing
An interesting example of an error:
double e = 2.718281828459045; object o = e; // box // int ee = (int)o; // calls a runtime exception, since it can be identified as the int type int ee = (int)(double)o; // performs unboxing without errors
Another example:
int count=1; object countObject=count; // the count value was copied into the countObject variable, it was boxing count+=1; // count equals 2 already, and the countObject value is still 1 count=(int)countObject; // now count also equals 1 – it was unboxing
The ?? operator is called a null-coalescing operator. It returns the left value, if it does not equal null, otherwise – the right value. An example:
int? x = null; // here int? means that it is the Nullable type i.е. null can be assigned to the object int y = x ?? -1; // In this case, y equals -1, since x equals null
The ? operator is called a conditional operator. It returns one of two values basing on the Boolean expression. If an expression equals true, the first value is returned; otherwise, the second value is returned. Here are 2 similar codes, for example:
if (input<0) // using the if-else construct classify = "negative"; else classify = "positive"; classify = (input < 0) ? "negative" : "positive"; // using the conditional operator ?:
LINQ
Any object that implements the IEnumerable or IQueryable interface can be queried using LINQ.We can use either
We can use either orderby…descending, or OrderByDescending.orderby h.State, h.City – here
orderby h.State, h.City – here orderby is written in one word, however, group h by h.State is written separately (there is also a variant of the capitalized GroupBy)Predicate is a condition that characterizes a subject. For example:
Predicate is a condition that characterizes a subject. For example:
where i % 2 == 0
There are 2 variants of writing LINQ (method syntax/method and query syntax/query):
var data=Enumerable.Range(1,100); var method=data.Where(x=>x%2==0).Select(x=>x.ToString()); var query=from d in data where d%2==0 select d.ToString();
Simple LINQ methods:
var values=new[]{“A”,”B”,”C”,”B”,”B”,”A”,”B”}; var distinct=values.Distinct(); // only non-unique values var first=values.First(); var firstordef=values.FirstOrDefault(); var twoval=values.Skip(2).Take(2);
There is an interesting option of using where instead of && several times. For example:
var evenNumbers = from i in myArray where i % 2 == 0 where i > 5 select i;
Functions can be used as a filter. For example:
var evenNumbers = from i in myArray where IsEvenAndGT5(i) select i; static bool IsEvenAndGT5(int i) { return (i % 2 == 0 && i > 5); }
If we need to select several values, we need to use new { }
var names = from p in people select new { p.FirstName, p.LastName };
We can even set aliases:
var names = from p in people select new { First = p.FirstName, Last = p.LastName }; var names = people.Select(p => new { p.FirstName, p.LastName }); // a variant of a query with the method syntax
LINQ deferred execution – a query is executed till the element is called. For example:
int[] myArray = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var evenNumbers = from i in myArray where i % 2 == 0 select i; foreach (int i in evenNumbers) { Console.WriteLine(i); // outputting the result of the LINQ query } myArray[1] = 12; // the array element, from which the selection with help of the query takes place, is being modified foreach (int i in evenNumbers) // here, another call of the LINQ query takes place Console.WriteLine(i); // 12, not 2 is outputted by the first element } // the next query is Immediate Execution. Data is retrieved at once var evenNumbersCount=(from i in myArray where i % 2 == 0 select i).Count();
The assembly version comprises of four parts: Major, Minor, Build, and Revision.
To install the GAC (Global Assembly Cache) assembly, the Global Assembly Cache tool (Gacutil.exe) is required. You need to enter the following line into the command line: gacutil –i.
But the assembly must be signed with a strong name key with help of SN. An example of key creation: «C:\GACKey\GACkey.snk» sn –k.
Strong name assembly consists of 5 parts: Friendly Name, Version, Culture, Public Key Token, and Processor Architecture.When you sign your assemblies, store your key in a safe place, and give developers only an open part of the key – it will allow them to use assemblies without a full signature.
When you sign your assemblies, store your key in a safe place, and give developers only an open part of the key – it will allow them to use assemblies without a full signature.You can highlight the open part of the key with the following command:
You can highlight the open part of the key with the following command:Sn.exe –p «key.snk» «open key.snk»
Sn.exe –p «key.snk» «open key.snk»Since the assembly version is important at the moment when the runtime is trying to find the assembly, you can publish various versions of the same assembly in GAC and evade problems with normal
Since the assembly version is important at the moment when the runtime is trying to find the assembly, you can publish various versions of the same assembly in GAC and evade problems with normal ddls.
It is called a side-by-side hosting at which many versions of the same assembly are hosted on the same computer (as from .NET 4).
Shallow copy – a trap when copying arrays
When copying arrays of the reference type, a pointer to the value can be copied instead of the value itself. An example:
Person[] orginal = new Person[1]; orginal[0] = new Person() { Name = "John" }; Person[] clone = (Person[])orginal.Clone(); clone[0].Name = "Mary"; // note that now both, original[0] and clone[0] contain “Mary” similar to the array of the reference type Person[] orginal = new Person[1]; orginal[0] = new Person() { Name = "John" }; Person[] clone = (Person[])orginal.Clone(); clone[0] = new Person() { Name = "Bob" }; // note that now original[0]=”John” and clone[0]=”Bob” // since the reference to a new element of the original array has been replaced with a newly created element
Closures
var funcs = new List<Func>(); for (int i = 0; i < 3; i++) { funcs.Add(() => i); } foreach (var f in funcs) Console.WriteLine(f()); // Will output 3 3 3, not 1 2 3, as it is expected
This result is conditioned by two reasons:
- During closures, variables, not variable values, are captured.
- The provided code fragment contains one instance of the i variable, that is modified at each loop iteration, instead of creation of a new instance at each iteration.
We can easily fix this situation:
var funcs = new List<Func>(); for (int i = 0; i < 3; ++i) { int tmp = i; // creation of a temporary variable funcs.Add(() => tmp); // When adding to List, the capture of temporary variable value takes place } foreach (var f in funcs) Console.WriteLine(f()); // ow, everything is ok, and we will get 1, 2, 3
Do you use compiler directives?Here are few examples of directives:
#region and #endregion – limit a certain part of code and allow rolling/unrolling.
#define and #undef – define true or false for certain values.
#error – generates an error
An example of a condition which will execute one code fragment in the Debug mode, and another fragment in another case:
#if DEBUG
#elif
#endif
What is PCL? Portable Component Library is a dll but with the cross-platform options. That is, you can create PCL and set platforms in which it will be used (e.g. WP8, WinRT, Xamarin… ). In fact, the addition of each platform limits the assembly capabilities.
PDB is an acronym for the Program database file.
PDB contains debugging data and data about the project state that allow the execution of sequential compilation of debugging function of the program.
Anyone who has access to the ddl/exe file can easily perform reverse engineering to generate the source code with PDB or without it using such tools, as a reflector. So, provision or non-provision of PDB does not have any influence on the code security.
Implicit and Explicit implementation
Suppose, that we have 2 interfaces with a similar method:
interface InterfaceOne { void InterfaceMethod(); } interface InterfaceTwo { void InterfaceMethod(); }
We create a class that is inherited from two interfaces and declare the interfaceMethod in it:
public class MyClass : InterfaceOne, InterfaceTwo { public void InterfaceMethod() { Console.WriteLine("Guess which interface method is called?"); } }
We used the Implicit interface implementation. And if we specify the interface name, it will turn into the explicit implementation:
public class MyClass : InterfaceOne, InterfaceTwo { void InterfaceOne.InterfaceMethod() { Console.WriteLine("Now it’s easy to guess which interface the method belogs to, isn’t it?"); } void InterfaceTwo.InterfaceMethod() { Console.WriteLine("Now it’s easy to guess which interface the method belongs to, isn’t it?"); } }
In the information science, reflection means a process during which a program can track and modify its own structure and behavior during execution. An example:
void Method() { var horse=new Animal(); var type=horse.GetType(); var method=type.GetMethod(“Speak”); var value=(string)method.Invoke(horse,null); // value=”hello”; } public class Animal { public string Speak() { return “hello”; } }
During reflection, the assembly can be loaded in several ways:
- Assembly.LoadFrom() can make redirection to other similar assembly.
- Assembly.LoadFile() loads the assembly from a file.
- Assembly.Load() loads the assembly basing on its full name specified.
The difference between the Convert and Parse methods is that Parse accepts row as an input value, while Convers can accept other data types.
The difference between Parse/TryParse and Convert is that Convert can accept null values and does not throw ArgumentNullException.
To provide positive and negative infinity, C# has two special constants: System.Double.NegativeInfinity and System.Double.PositiveInfinity.NegativeInfinity — the value of this constant is the result of
NegativeInfinity — the value of this constant is the result of division of a negative number by null. Also, this constant is returned, if the operation result is less than the minimum value (MinValue).PositiveInfinity is the result of
PositiveInfinity is the result of division of a positive number by 0. The constant is returned, if the value is greater than MaxValue.Infinity includes both, NegativeInfinity and PositiveInfinity,
Infinity includes both, NegativeInfinity and PositiveInfinity,The unchecked keyword is used to suppress the overflow checking during execution of arithmetic operations and transformation of integer data. If the unchecked environment is deleted, the compilation error occurs. 2 examples:
The unchecked keyword is used to suppress the overflow checking during execution of arithmetic operations and transformation of integer data. If the unchecked environment is deleted, the compilation error occurs. 2 examples:
unchecked { int1 = 2147483647 + 10; } int1 = unchecked(2147483647 + 10);
By default, C# does not throw exception, if during the narrowing transformation, the error with the int and float types occurs.
To make this exception occur, we can use the checked keyword. An example:
checked { int big = 1000000; short small = (short)big; }
Also, we can put the check-mark in the project properties to throw an exception during overflow/underflow.
Formatting
Console.WriteLine(somevariable.ToString("c")) // formats into the currency format // for example, by spcifying somevariable.ToString("C3", new System.Globalization.CultureInfo("fr-FR")) if somevariable=123.4562, we will get -123,456 € // ToString("d") or ToString("D") formats number as decimal // we can specify the number as a minimum number of character. For example, if we specify ("D6"), we will get -001234 from -001234 DateTime.Now.ToString("D") // if date is a parameter, it formats into a short data format = DateTime.Now.ToShortDateString() w.ToShortDateString() DateTime.Now.ToString("d") // long date format
Rethrow exception
The error stack is cleared every time when the error is thrown.
static string ReadAFile(string fileName) { string result = string.Empty; try { result = File.ReadAllLines(fileName); } catch(Exception ex) { throw ex; // This will output ReadAFile in StackTrace – not quite the correct solution throw; // his will also output ReadAllLines in StackTrace – it is better to use this variant }
It is recommended to use just throw, since in this case, information about initial error is saved.
Optionally, we can create our own error – custom exception.
[Serializable()] public class YourCustomException : System.Exception { // the constructor takes the text as a parameter public YourCustomException(string msg) : base(msg) { } }
After that, we can throw the error:
try { throw new YourCustomException("You cannot enter text in this field"); // some code } catch(YourCustomException e){}
It is not recommended to inherit from System.ApplicationException. Notionally, it was supposed that all C# runtime errors must be inherited from System.Exception, and all custom exceptions – from System.ApplicationException. But in practice, the System.ApplicationException was not used at all.In the
In the try/catch/finally block, finally is always executed. Except the case of calling Environment.FailFast(String) or FailFast(String, Exception) in the code. They interrupt the current process in the cases of any critical damages of the application work, and the program execution will be interrupted, and finally will not be executed.
CodeDOM is a set of classes that allow generating code.
The System.CodeDom.CodeCompileUnit is a top-level class, a container for all objects in the generated code.
The System.CodeDom.CodeDOMProvider class generates the file class in C#, VB or JScript.
Similar to classes, structs can have methods, properties, constructors, etc.
Unlike classes, structs cannot contain destructors (in fact, destructors are the Finalize methods of the ~CalssName that allow destroying an object).
You cannot declare an empty constructor for a struct. Also, structs cannot have inheritance hierarchies (for the purpose of memory saving).
Also, structs cannot have inheritance hierarchies (for the purpose of memory saving). In C#, structs are implicitly sealed. It means that a struct cannot be inherited from.
Structures – value data type = stored in stack.
Classes – value data type = stored in heap.
In C#, structs and enumerations are of the value data type .Structs, in their turn, are divided into the following subcategories: numeric type (integral types, floating-point types, 128-bit decimals), boolean type and user structs.
Structs, in their turn, are divided into the following subcategories: numeric type (integral types, floating-point types, 128-bit decimals), boolean type and user structs.
Enumerations are sets of types declared with the enum keyword.
The value type has two limitations:
- You cannot inherit from the value type
- Value type cannot accept null value
enum Months {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sept, Oct, Nov, Dec}; // instead of int, we can use another numeric type for enum enum Months : byte {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sept, Oct, Nov, Dec}; string name = Enum.GetName(typeof(Months), 8); Console.WriteLine("Eighth month in enum is " + name); Console.WriteLine("Numeric values Months enum:"); foreach (int values in Enum.GetValues(typeof(Months))) { Console.WriteLine(values); }
The Interlocked class provides atomic operations for variables that are available fro several threads (Decrement(Int), Increment(Int), Add(Int, Int) operations). The variable value will be changed simultaneously for all threads.The Compare Exchange method compares two values for equality. If they are equal, it replaces one the values.
The Compare Exchange method compares two values for equality. If they are equal, it replaces one the values.
Interlocked.CompareExchange(ref value, newvalue, comparevalue)
The Exchange method sets variable by a specified value, similar to the atomic operation, returns the initial values. An example:
if (Interlocked.Exchange(ref isInUse, 1) ==0){ // some code }
Parameters passed to functions
static int CalculateBMI(int weight, int height) { return (weight * 703) / (height * height); }
Normally, the function is called in the following way: CalculateBMI(123, 64);If we don’t remember the order, we can call the function by specifying names:
If we don’t remember the order, we can call the function by specifying names:
CalculateBMI(weight: 123, height: 64); CalculateBMI(height: 64, weight: 123);
First, we need to specify a parameter by order, and then – by name:
CalculateBMI(123, height: 64);
You cannot specify the first parameter by name, and then – by order (leads to an error)
//CalculateBMI(weight: 123, 64);
We can specify default values for parameters and then call the function without indicating these parameters:
public void ExampleMethod(int required, string optionalstr = "default string", int optionalint = 10)
Using StringWriter and StringReader for writing and reading strings
StringWriter strwtr = new StringWriter(); for(int i=0; i < 10; i++) strwtr.WriteLine("This is i: " + i); StringReader strrdr = new StringReader(strwtr.ToString()); // Now we read from StringReader. string str = strrdr.ReadLine(); while(str != null) { str = strrdr.ReadLine(); Console.WriteLine(str);
Comparison of Generics and ArrayList
var objects=new ArrayList(); objects.Add(1); objects.Add(2); objects.Add(“three”); // ArrayList allows various data types var list=new List<int>(); list.Add(1); list.Add(2); list.Add(“three”); // the latter is unacceptable, since the generic collection in this case allows only int data types
Regex (regular expressions)
An example of IsMatch:
if (Regex.IsMatch(phone, @"^\d{3}-\d{4}$"))
Matches – searches for all entries of a regular expression in the input string and returns all the matches:
string pattern = @"\b\w+es\b"; Regex rgx = new Regex(pattern); string sentence = "Who writes these notes?"; foreach (Match match in rgx.Matches(sentence)) Console.WriteLine("Found '{0}' at position {1}", match.Value, match.Index);
Match works similar to Matches, but searches for the first match only.
Split divides the input string into the substring array in the positions defined by correspondence of the regular expression.
string input = «plum-pear»;
string pattern = “(-)”;
string[] substrings = Regex.Split(input, pattern);
Replace – replaces all strings in the input string that correspond to the template of the regular expression with the specified change string.
^ — start of the string, $ — end of the string. – any symbol
* — repetitions of the previous 0 or several times
+ — repetition of the previous 1 or several times
? – repetition of the previous 0 or 1 time
[abc] – any of a, b or c symbols, [^abc] – any symbol except these, [a-z] – symbols from…to, [^a-z] – symbols except from…to
{n} – match of n repetitions, {n,} – match from n to infinity, {n,m} – match from n to m times,
\d – a number, \D – not a number, \s – space, tab etc, \S – not space
Few Tricks
In spite of prohibition of exam info disclosure, you can easily find dumps (question information leaks) on the web. Are they worth of using? Do they really help? Taking into account blog posts, many people do use dumps. IMO, it is better not to stick to them. Firstly, they contain many mistakes. Also, do not forget that questions change from time to time. Can you cheat in the exam? Well, you could be lucky enough with the questions, but we are interested in knowledge not in a paper, aren’t we?
A legal way to find question examples is to look through the questions in the books, like “MCSD Certification Toolkit Exam 70-483 Programming in C#” and “Wouter de Kort Programming in C# Exam Ref 70-483”. I would recommend you to pay attention to the very subject of the question subject, not the options for an answer. In this way, you will learn nuances and work on unknown aspects.
As for video tutorial, I recommend a course at MVA (the portal is absolutely free) – www.microsoftvirtualacademy.com/training-courses/developer-training-with-programming-in-c
There is also an interesting training game at www.codehunt.com. It will not prepare you for the test, but may be interesting as a warm-up.
The best legal way to improve your knowledge and grade is to look though and think over all questions after passing the test.
That’s it. I wish everyone good grades and great knowledge in C#!
Tags: c#, tips and tricks Last modified: September 23, 2021