Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Ternary

I was read I was read 0

View Demo

To avoid multiple invocations equivalent would be:

var interimResult = foo.Bar;

Console.WriteLine(interimResult != null ? interimResult.Length : (int?)null);

And this di erence somewhat explains why null propagation operator is not yet supported in expression trees.

Section 72.5: Expression-bodied function members

Expression-bodied function members allow the use of lambda expressions as member bodies. For simple members, it can result in cleaner and more readable code.

Expression-bodied functions can be used for properties, indexers, methods, and operators.

Properties

public decimal TotalPrice => BasePrice + Taxes;

Is equivalent to:

public decimal TotalPrice

{

get

{

return BasePrice + Taxes;

}

}

When an expression-bodied function is used with a property, the property is implemented as a getter-only property.

View Demo

Indexers

public object this[string key] => dictionary[key];

Is equivalent to:

public object this[string key]

{

get

{

return dictionary[key];

}

}

GoalKicker.com – C# Notes for Professionals

429

Methods

static int Multiply(int a, int b) => a * b;

Is equivalent to:

static int Multiply(int a, int b)

{

return a * b;

}

Which can also be used with void methods:

public void Dispose() => resource?.Dispose();

An override of ToString could be added to the Pair<T> class:

public override string ToString() => $"{First}, {Second}";

Additionally, this simplistic approach works with the override keyword:

public class Foo

{

public int Bar { get; }

public string override ToString() => $"Bar: {Bar}";

}

Operators

This also can be used by operators:

public class Land

{

public double Area { get; set; }

public static Land operator +(Land first, Land second) => new Land { Area = first.Area + second.Area };

}

Limitations

Expression-bodied function members have some limitations. They can't contain block statements and any other statements that contain blocks: if, switch, for, foreach, while, do, try, etc.

Some if statements can be replaced with ternary operators. Some for and foreach statements can be converted to LINQ queries, for example:

IEnumerable<string> Digits

{

get

{

for (int i = 0; i < 10; i++) yield return i.ToString();

}

}

GoalKicker.com – C# Notes for Professionals

430

IEnumerable<string> Digits => Enumerable.Range(0, 10).Select(i => i.ToString());

In all other cases, the old syntax for function members can be used.

Expression-bodied function members can contain async/await, but it's often redundant:

async Task<int> Foo() => await Bar();

Can be replaced with:

Task<int> Foo() => Bar();

Section 72.6: Operator nameof

The nameof operator returns the name of a code element as a string. This is useful when throwing exceptions related to method arguments and also when implementing INotifyPropertyChanged.

public string SayHello(string greeted)

{

if (greeted == null)

throw new ArgumentNullException(nameof(greeted));

Console.WriteLine("Hello, " + greeted);

}

The nameof operator is evaluated at compile time and changes the expression into a string literal. This is also useful for strings that are named after their member that exposes them. Consider the following:

public static class Strings

{

public const string Foo = nameof(Foo); // Rather than Foo = "Foo" public const string Bar = nameof(Bar); // Rather than Bar = "Bar"

}

Since nameof expressions are compile-time constants, they can be used in attributes, case labels, switch statements, and so on.

It is convenient to use nameof with Enums. Instead of:

Console.WriteLine(Enum.One.ToString());

it is possible to use:

Console.WriteLine(nameof(Enum.One))

The output will be One in both cases.

The nameof operator can access non-static members using static-like syntax. Instead of doing:

string foo = "Foo";

string lengthName = nameof(foo.Length);

GoalKicker.com – C# Notes for Professionals

431

Can be replaced with:

string lengthName = nameof(string.Length);

The output will be Length in both examples. However, the latter prevents the creation of unnecessary instances.

Although the nameof operator works with most language constructs, there are some limitations. For example, you cannot use the nameof operator on open generic types or method return values:

public static int Main()

{

Console.WriteLine(nameof(List<>)); // Compile-time error Console.WriteLine(nameof(Main())); // Compile-time error

}

Furthermore, if you apply it to a generic type, the generic type parameter will be ignored:

Console.WriteLine(nameof(List<int>)); // "List"

Console.WriteLine(nameof(List<bool>)); // "List"

For more examples, see this topic dedicated to nameof.

Workaround for previous versions (more detail)

Although the nameof operator does not exist in C# for versions prior to 6.0, similar functionality can be had by using MemberExpression as in the following:

Version < 6.0

Expression:

public static string NameOf<T>(Expression<Func<T>> propExp)

{

var memberExpression = propExp.Body as MemberExpression;

return memberExpression != null ? memberExpression.Member.Name : null;

}

public static string NameOf<TObj, T>(Expression<Func<TObj, T>> propExp)

{

var memberExpression = propExp.Body as MemberExpression;

return memberExpression != null ? memberExpression.Member.Name : null;

}

Usage:

string variableName = NameOf(() => variable); string propertyName = NameOf((Foo o) => o.Bar);

Note that this approach causes an expression tree to be created on every call, so the performance is much worse compared to nameof operator which is evaluated at compile time and has zero overhead at runtime.

GoalKicker.com – C# Notes for Professionals

432