Interview: C#_Part 1

vishal gupta
9 min readNov 18, 2020

--

Use the var keyword to let the compiler infer the type

Casting and Type Conversions — A type conversion that does not cause data loss is performed automatically by the compiler. A conversion that might cause data loss requires a cast in the source code.

The common type system — CTS

All types, including built-in numeric types such as System.Int32 (C# keyword: int), derive ultimately from a single base type, which is System.Object (C# keyword: object).

Each type in the CTS is defined as either a value type or a reference type.

Types that you define by using the struct keyword are value types; all the built-in numeric types are structs. Types that you define by using the class keyword are reference types.

Value type variables directly contain their values, which means that the memory is allocated inline in whatever context the variable is declared. There is no separate heap allocation or garbage collection overhead for value-type variables.

Nullable value types

Ordinary value types cannot have a value of null. However, you can create nullable value types by affixing a ? after the type. For example, int? is an int type that can also have the value null. Nullable value types are especially useful when you are passing data to and from databases in which numeric values might be null.

Nullable reference types

With the addition of nullable reference types, you can declare your intent more clearly. The null value is the correct way to represent that a variable doesn't refer to a value.

string? name;

string? TryGetMessage(string key);

Nullability of types

  • Nonnullable: Null can’t be assigned to variables of this type. Variables of this type don’t need to be null-checked before dereferencing.
  • Nullable: Null can be assigned to variables of this type. Dereferencing variables of this type without first checking for null causes a warning. You can override this behavior by using the null-forgiving operator ! following a variable name.
name!.Length;

Dereferencing a pointer means getting the value that is stored in the memory location pointed by the pointer.

Late-initialized properties, Data Transfer Objects and nullability

Consider the following DTO class, prior to enabling nullable reference types, that represents a student:

class Student
{
[Required]
public string FirstName { get; set; }

[Required]
public string LastName { get; set; }

public string VehicleRegistration { get; set; }
}

When you enable nullable reference types, you want to indicate on our DTO which of the properties may be nullable, consistent with your original intent:

class Student
{
[Required]
public string FirstName { get; set; }

[Required]
public string LastName { get; set; }

public string? VehicleRegistration { get; set; }
}

For this DTO, the only property that may be null is VehicleRegistration.

However, the compiler raises CS8618 warnings for both FirstName and LastName, indicating the non-nullable properties are uninitialized.

Initialize the property to null with the help of the null-forgiving operator (!):

[Required]
public string FirstName { get; set; } = null!;

[Required]
public string LastName { get; set; } = null!;

public string? VehicleRegistration { get; set; }

Generic definitions and nullability

Correctly communicating the null state of generic types and generic methods requires special care. This stems from the fact that a nullable value type and a nullable reference type are fundamentally different. An int? is a synonym for Nullable<int>, whereas string? is string with an attribute added by the compiler. The result is that the compiler can't generate correct code for T? without knowing if T is a class or a struct.

What it does mean is that you can’t use T? in a generic class or method declaration without constraints.You can overcome this limitation by adding either the struct or class constraint. With either of those constraints, the compiler knows how to generate code for both T and T?.

This doesn’t mean you can’t use a nullable type (either value type or reference type) as the type argument for a closed generic type. Both List<string?> and List<int?> are valid instantiations of List<T>.

Generic types

Generic collection classes are called strongly typed collections because the compiler knows the specific type of the collection’s elements and can raise an error at compile-time if, for example, you try to add an integer to the strings object in the previous example.

List<string> strings = new List<string>();

Implicit types, anonymous types, and tuple types

Local variables can be declared without giving an explicit type. The var keyword instructs the compiler to infer the type of the variable from the expression on the right side of the initialization statement.

// i is compiled as an int

var i = 5;

Anonymous Types

Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first.

You create anonymous types by using the new operator together with an object initializer.

var v = new { Amount = 108, Message = “Hello” };

// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and string.
Console.WriteLine(v.Amount + v.Message);

Tuple types

The tuples feature provides concise syntax to group multiple data elements in a lightweight data structure.

(double, int) t1 = (4.5, 3);
Console.WriteLine($”Tuple with elements {t1.Item1} and {t1.Item2}.”);
// Output:
// Tuple with elements 4.5 and 3.

(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($”Sum of {t2.Count} elements is {t2.Sum}.”);
// Output:
// Sum of 3 elements is 4.5.

Tuple field names

var t = (Sum: 4.5, Count: 3);

Console.WriteLine($”Sum of {t.Count} elements is {t.Sum}.”);

Use cases of tuples

One of the most common use cases of tuples is as a method return type. That is, instead of defining out method parameters, you can group method results in a tuple return type

var ys = new[] { -9, 0, 67, 100 };
var (minimum, maximum) = FindMinMax(ys);
Console.WriteLine($”{minimum} and {maximum}”);
// Output:
// -9 and 100

(int min, int max) FindMinMax(int[] input) {

return (min, max);

}

Tuples vs System.Tuple

C# tuples, which are backed by System.ValueTuple types, are different from tuples that are represented by System.Tuple types. The main differences are as follows:

  • ValueTuple types are value types. Tuple types are reference types.
  • Data members of ValueTuple types are fields. Data members of Tuple types are properties.

The Common type system

It is important to understand two fundamental points about the type system in .NET:

  • It supports the principle of inheritance. Types can derive from other types, called base types.
  • Each type in the CTS is defined as either a value type or a reference type.

Garbage collection

Automatic memory management functionality of the CLR, which is known as garbage collection.

Abstract

A class can be declared abstract. An abstract class contains abstract methods that have a signature definition but no implementation. Abstract classes cannot be instantiated.

Tuple

A tuple provides a lightweight way to retrieve multiple values from a method call.

using System;

public class Example
{
public static void Main()
{
var result = QueryCityData(“New York City”);

var city = result.Item1;
var pop = result.Item2;
var size = result.Item3;

// Do something with the data.
}

private static (string, int, double) QueryCityData(string name)
{
if (name == “New York City”)
return (name, 8175133, 468.48);

return (“”, 0, 0);
}
}

Deconstructing tuple elements with discards

Starting with C# 7.0, you can take advantage of C#’s support for discards, which are write-only variables whose values you’ve chosen to ignore.

Interface

An interface contains definitions for a group of related functionalities that a non-abstract class or a struct must implement.

Interfaces can contain instance methods, properties, events, indexers, or any combination of those four member types.

Interfaces may contain static constructors, fields, constants, or operators.

An interface can’t contain instance fields, instance constructors, or finalizers. Interface members are public by default.

A base class can also implement interface members by using virtual members. In that case, a derived class can change the interface behavior by overriding the virtual members. For more information about virtual members, see Polymorphism.

Methods

You can also use named arguments instead of positional arguments when invoking a method.

var travelTime = moto.Drive(speed: 60, miles: 170);

Optional parameters and arguments

If a method includes both required and optional parameters, optional parameters are defined at the end of the parameter list, after all required parameters.

The use of optional parameters affects overload resolution — preference goes to a candidate that does not have optional parameters

Return values

Sometimes, you want your method to return more than a single value. Starting with C# 7.0, you can do this easily by using tuple types and tuple literals.

Arrays can be passed as arguments to method parameters. Because arrays are reference types, the method can change the value of the elements.

Extension methods

Extension methods let you “add” a method to an existing type without modifying the type itself or implementing the new method in an inherited type.

Async Methods

By using the async feature, you can invoke asynchronous methods without using explicit callbacks or manually splitting your code across multiple methods

When control reaches an await expression in the async method, control returns to the caller if the awaited task is not completed, and progress in the method with the await keyword is suspended until the awaited task completes. When the task is complete, execution can resume in the method.

An async method can have a return type of Task<TResult>, Task, or void. The void return type is used primarily to define event handlers, where a void return type is required. An async method that returns void can't be awaited, and the caller of a void-returning method can't catch exceptions that the method throws.

An async method can’t declare any in, ref, or out parameters, but it can call methods that have such parameters.

Expression-bodied members =>

It is common to have method definitions that simply return immediately with the result of an expression, or that have a single statement as the body of the method. There is a syntax shortcut for defining such methods using =>:

public void Print() => Console.WriteLine(First + “ “ + Last);

public Point Move(int dx, int dy) => new Point(x + dx, y + dy);

public string Name => First + “ “ + Last;

Iterators

An iterator performs a custom iteration over a collection, such as a list or an array. An iterator uses the yield return statement to return each element one at a time. When a yield return statement is reached, the current location is remembered so that the caller can request the next element in the sequence.

Properties

Properties behave like fields when they are accessed. A field defines a storage location.

Properties are a form of smart fields in a class or object. From outside the object, they appear like fields in the object. You can provide validation, different accessibility, lazy evaluation, or any requirements your scenarios need.

public string FirstName { get; set; } = string.Empty;

When a property implementation is a single expression, you can use expression-bodied members for the getter or setter:

Read-only

You could give the set accessor private accessibility instead of public

You could not declare a private property with a public accessor.

Computed properties

Attaching attributes to auto-implemented properties

The NonSerializedAttribute can only be attached to fields, not properties. You can attach the NonSerializedAttribute to the backing field for the Id property by using the field: specifier on the attribute, as shown in the following example:

Implementing INotifyPropertyChanged

When the value of a property changes, the object raises the INotifyPropertyChanged.PropertyChanged event to indicate the change.

Null Conditional Operator

The ?. operator is called the null conditional operator. It checks for a null reference before evaluating the right side of the operator.

It would throw a NullReferenceException without this check in that case.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

vishal gupta
vishal gupta

Written by vishal gupta

Software Architect, Author, Trainer

No responses yet

Write a response