📜 ⬆️ ⬇️

Sugar injections in C #

C # is a thoughtful and developed programming language that provides a lot of syntactic sugar that simplifies writing routine code. Still, there are a number of scenarios where you need to show some ingenuity and ingenuity in order to maintain harmony and beauty.

In the article we will consider some such cases, both widely known and not so much.


Declaration and event call

Sometimes at the interview they may ask a question how to trigger events correctly?
The classic way to declare an event is as follows:
')
public event EventHandler HandlerName; 

and the call itself is:

 var handler = HandlerName; if (handler != null) handler(o, e); 

We copy the handler to a separate handler variable, as this protects against NullReferenceException in multi-threaded applications. After all, when recording

 if (HandlerName != null) HandlerName(o, e); 

another thread may unsubscribe from the event after checking for null , which will lead to an exception if it was the only or last subscriber.

But there is a more concise way without additional checks:

 public event EventHandler HandlerName = (o, e) => { }; 

 public event EventHandler HandlerName = delegate { }; 

 HandlerName(o, e); 

Unsubscribing from an empty delegate is impossible, so HandlerName is guaranteed not to be null .

For the sake of fairness, it is worth noting that in multi-threaded applications both of these methods can lead to calling an event handler for an object that has already unsubscribed, therefore this behavior should be provided on the subscriber side.

Patterns INotifyPropertyChanging and INotifyPropertyChanged

Quite often, the following record is used to notify about changes in property values:

 private string _name; public string Name { get { return _name; } set { _name = value; var handler = PropertyChanged; if (handler != null) PropertyChanged(this, new PropertyChangedEventArgs("Name")); } } 

It can hardly be called concise, and the string constant with the name of the property does not look very nice. Therefore, a more elegant notification method based on lambda expressions was developed.

 public string Name { get { return Get(() => Name); } set { Set(() => Name, value); } } 

Sometimes you can hear the objection that this method is slow. Yes, in synthetic tests it is inferior to the first, but in real applications there is no any noticeable performance degradation, because it is very rare when the property changes with great frequency and you need to track every such change. In addition, it is acceptable to combine various methods of notification, so the option with lambda expressions is very good in practice.

The usual subscription to change notifications looks like this:

 PropertyChanged += (o, e) => { if (e.PropertyName != "Name") return; // do something } 

However, there is another option worth noting with overloading the indexer:

 this[() => Name].PropertyChanging += (o, e) => { // do somethig }; this[() => Name].PropertyChanged += (o, e) => { // do somethig }; 

 viewModel[() => viewModel.Name].PropertyChanged += (o, e) => { // do somethig }; 

If you need to perform a few actions, you can easily keep within one line of code:

 this[() => Name].PropertyChanged += (o, e) => SaveChanges(); 

With the help of the same approach, validation of properties in combination with the IDataErrorInfo pattern is conveniently implemented.

 this[() => Name].Validation += () => Error = Name == null || Name.Length < 3 ? Unity.App.Localize("InvalidName") : null; 

In this case, there is no need to worry about performance either, since the lambda expression is analyzed only once during the subscription itself.

Chain Type Reduction by the Method Of

Sometimes there are situations when you need to perform several type conversions in a row:

 ((Type2)((Type1)obj).Property1)).Property2 = 77; 

The number of brackets rolls over and readability falls. Generic-method-extension comes to the rescue
 Of<TType>() 
.

 obj.Of<Type1>().Property1.Of<Type2>.Property2 = 77; 

Its implementation is very simple:

  public static class Sugar { public static T Of<T>(this object o) { return (T) o; } public static bool Is<T>(this object o) { return o is T; } public static T As<T>(this object o) where T : class { return o as T; } } 

ForEach

Have class
 List<TItem> 
There is a convenient ForEach method, but it is useful to extend it for collections of other types

  public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action) { foreach (var item in collection) { action(item); } } 

Now some operations can be described only in one line, without resorting to the ToList () method.
 persons.Where(p => p.HasChanged).ForEach(p => p.Save()); 

Sync await

Asynchronous programming with async / await is a huge step forward, but in rare cases, for example, for backward compatibility you need to turn asynchronous methods into synchronous ones. This will help a small class adapter.

  public static class AsyncAdapter { public static TResult Await<TResult>(this Task<TResult> operation) { // deadlock safe variations // var result = default(TResult); // Task.Factory.StartNew(async () => result = await operation).Wait(); // return result; // return Task.Run(() => operation.Result).Result; return operation.Result; } public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) { return operation.AsTask().Result; } public static TResult Await<TResult, TProgress>(this IAsyncOperationWithProgress<TResult, TProgress> operation) { return operation.AsTask().Result; } } 

Its application is very simple:

 // var result = await source.GetItemsAsync(); var result = source.GetItemsAsync().Await(); 

Teams

xaml-oriented developers are familiar with the ICommand pattern. As part of the MVVM approach, there are various implementations of it. But in order to correctly implement the pattern, it is necessary to take into account the fact that the visual control usually subscribes to the command's CanExecuteChanged event, which can lead to memory leaks when using dynamic interfaces. All this often leads to a complication of command syntax.

Of interest is the concept of context-oriented teams.

 public class HelloViewModel : ContextObject, IExposable { public string Message { get { return Get(() => Message); } set { Set(() => Message, value); } } public virtual void Expose() { this[() => Message].PropertyChanged += (sender, args) => Context.Make.RaiseCanExecuteChanged(); this[Context.Make].CanExecute += (sender, args) => args.CanExecute = !string.IsNullOrEmpty(Message); this[Context.Make].Executed += async (sender, args) => { await MessageService.ShowAsync(Message); }; } } 

 <Window DataContext="{Store Key=viewModels:HelloViewModel}"> <StackPanel> <TextBox Text="{Binding Message, Mode=TwoWay}"> <Button Content="{Localizing Make}" Command="{Context Key=Make}"> </StackPanel> </Window> 

Moreover, contextual commands are compatible with Routed Commands in WPF .

 <Button Command="New"/> 

 this[ApplicationCommands.New].Executed += (o, e) => { ... }; 

It is also important that command handlers can easily be both synchronous and asynchronous.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All these sweets are implemented in the Aero Framework library, the new version of which is available by reference ( backup link ), where you can see them live and in action.

Source: https://habr.com/ru/post/255759/


All Articles