📜 ⬆️ ⬇️

C # Acceptance

The multifaceted Sherlock Holmes and Erast Fandorin, the ideal aristocrat, honored the deductive method very much and both achieved amazing success in its application. “Drop all the impossible, what remains, and will be the answer, no matter how incredible it may seem,” said Sir Arthur Conan Doyle in the mouth of his hero.

However, the science of reasoning does not end on deductive reasoning - we should not forget about induction. About it and about the approximate matters and will treat this treatise.

Duck test


Induction is a process of logical reasoning based on the transition from particular to general provisions. The famous "duck test" is a prime example of such a process:

If it walks like a duck and quacks
(If something walks like a duck and quacks like a duck, then it's a duck)

')
As applied to programming languages ​​- in particular, to object-oriented ones, the “duck test” acquires a special meaning. In dynamically typed languages, the semantics of using an object is determined by the current set of its methods and properties. For example, in a statically typed language that does not support utilization, you can create a function that takes a Duck object as a parameter and calls the object's Quack and Walk methods. In the same utipizirovannom (and, as a result, in dynamically typed) language, a similar function can take as a parameter some abstract object and try to call the same methods in it (perhaps, checking their presence). In a statically typed language, this behavior can be achieved using inheritance and interface implementations, but this will be an expression of the is-a form, unlike has-a in dynamic languages.

Briefly explaining what is the utilization and why it is not available in statically typed languages, I will now slightly refute myself. For some, it may not be a revelation, but nonetheless. Some kind of duck typing is in C #. That's about it now in more detail.

foreach


Syntactic sugar may be a little sweeter than it seems at first glance. In particular, this concerns the foreach operator. Most textbooks say that to support foreach semantics in a class, the System.Collections.IEnumerable interface must be implemented (explicitly or implicitly). In fact, this is not a completely valid statement - if we talk about C #, then the mentioned interface is completely optional to implement.

If you open the C # Language Specification and look at section 8.8.4, you can see the following:

It is a collection of the type of the iteration variable. If expression has the value of a null , a System.NullReferenceException is thrown.
It can be used to make it out of the following criteria:
  • C contains a public instance method for the signature of the following text.
  • E contains a public instance method with the signature MoveNext() and the return type bool .
  • The current instance of the current value. This is the type of the collection type.

Thus, the following code will perfectly compile and output to the console what it is supposed to do:

using System;
using System.Collections. Generic ;

namespace DuckTyping
{
class Collection
{
public Enumerator GetEnumerator()
{
return new Enumerator();
}
}

class Enumerator
{
private bool moveNext = true ;

public object Current
{
get { return "Enumerator.Current" ; }
}

public bool MoveNext()
{
if (!moveNext)
return false ;

moveNext = false ;
return true ;
}
}

class Program
{
static void Main()
{
foreach ( object o in new Collection())
Console .WriteLine(o);
}
}
}

* This source code was highlighted with Source Code Highlighter .

Trifle, I'm nice.

Collection Initializers


Pretty nice innovation C # 3.0. I recall:

var digits = new List < int > { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

* This source code was highlighted with Source Code Highlighter .

All anything, but it remains to define the term "Collection". And with this there are problems.

The System.Collections.ICollection interface prior to .NET 2.0 was a completely stupid creation. In 2.0, it was repaired: System.Collections.Generic.ICollection<T> allows you to add and remove elements, traverse the foreach mentioned above and find out the number of elements in the collection.

It would seem that a solution was found: any class that implements the ICollection<T> will be called a collection. But it was not there - practically no one implements it. Of the thousands of classes. NET BCL, only a few dozen can boast that they have a publicly accessible constructor without parameters and can be brought to ICollection<T> .

Nobody thought of giving up in the C # Design Group. It was decided to use the already well-proven Pattern-based syntax - namely, to call the Add() method, if it exists at all. Now the requirements for the collection from the point of view of the Collection Initializer are the following: the implementation of IEnumerable and the presence of at least one public method Add() .

The item "the presence of at least one ..." requires a little explanation. The list of elements enclosed in braces is not a “list of elements to add” - this is “a list of arguments for Add() methods”. The argument list can thus be heterogeneous. In other words, having such a class:

public class Plurals : IDictionary< string , string >
{
public void Add( string singular, string plural);
public void Add( string singular);
public void Add(KeyValuePair< string , string > pair);
}


* This source code was highlighted with Source Code Highlighter .

The following initializer will be completely legal:

var myPlurals = new Plurals{ “collection”, { “query”, “queries” }, new KeyValuePair(“child”, “children”) };

* This source code was highlighted with Source Code Highlighter .

Since overload resolution is performed separately for each element from the initialization list is performed separately, all three methods will be involved.

Instead of conclusion


Sherlock Holmes, Erast Petrovich and your humble servant bow down. I hope it was interesting.

Thank you Dr. T for food for thought.

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


All Articles