📜 ⬆️ ⬇️

8 things you might not know about C #

Here are some unusual facts about C # that only a few developers know about.

1. Indexers can use params parameters.


We all know how indexers x = something ["a"] usually look, as well as the code necessary for its implementation:

public string this[string key] { get { return internalDictionary[key]; } } 

But did you know that you can use params parameters x = something ["a", "b", "c", "d"] to access the elements?
Just write your indexer like this:
')
 public IEnumerable<string> this[params string[] keys] { get { return keys.Select(key => internalDictionary[key]).AsEnumerable(); } } 

The great news is that you can use several indexers together in the same class. If someone passes an array or several arguments as a parameter, it will get an IEnumerable as the result, but by calling, the indexer with one parameter will get a single value.

2. String literals defined in your code are stored in a single copy.


Many developers think that the following code:

 if (x == "" || x == "y") 

will create a couple of lines each time. No, it will not. C #, like many other programming languages, uses string interning, and each literal string used in your application is put in a special list (hash table), called a string pool, whence the reference to it is taken at runtime.

You can use the String.IsInterned method to determine whether the string is currently in the internment pool, but remember that the expression String.IsInterned ("what") == "what" will always be true.
String.IsInterned ("wh" + "at") == "what" will also always be true, thanks to the compiler for the optimization. String.IsInterned (new string (new char [] {'w', 'h', 'a', 't'}) == new string (new char [] {'w', 'h', 'a' , 't'}) will only be true if the literal string “what” has already been used in your code, or if it was manually added to the internment pool.

If you have classes that regularly use strings, consider using the String.Intern method to add them to the string pool. However, be careful, as in this case they are stored until the application is completed, so use String.Intern carefully. To add a string to the pool, simply call the String.Intern method (someClass.ToString ()). Another precaution is that (object) "Hi" == (object) "Hi" is always true, thanks to the internment. Try to check this code in the interpretation window, and the result will be negative, since the debugger does not intern your lines.

3. Reduction of types to less specialized ones does not interfere with the use of their real type.


A great example of this is the property that returns an IEnumerable, whose actual type is List, for example:

 private readonly List<string> internalStrings = new List<string>(); public IEnumerable<string> AllStrings { get { return internalStrings; } 

You think that no one can change the list of strings. Alas, this is too easy to do:

 ((List<string>)x.AllStrings).Add("Hello"); 

Even the AsEnumerable method does not help here. You can use the AsReadOnly method, which creates a shell above the list and throws an exception every time you try to change it, however, and provides a good pattern for things like this with your classes.

4. Variables in methods can have a scope limited by curly brackets.


In the Pascal programming language, you must describe all the variables that will be used at the beginning of your function. Fortunately, today variable declarations can exist next to their assignment, which prevents them from being used accidentally before you intend.

However, this does not prevent you from using them after they are no longer necessary. We can describe temporary variables using the for / if / while / using keywords. You will be surprised, but you can also describe temporary variables using curly brackets without keywords to get the same result:

 private void MultipleScopes() { { var a = 1; Console.WriteLine(a); } { var b = 2; Console.WriteLine(a); } } 

This code is not compiled because of the second line, because it refers to a variable that is enclosed in braces. Such a method splitting into parts with curly braces is quite useful, but a much better solution is to divide the method into smaller ones using the refactoring method: extract method.

5. Enumerations may have extension methods.


Extension methods provide the ability to write methods for existing classes, so that people on your team can use them. Considering that enumerations are similar to classes (more precisely, structures) you should not be surprised that they can be extended, for example:

 enum Duration { Day, Week, Month }; static class DurationExtensions { public static DateTime From(this Duration duration, DateTime dateTime) { switch (duration) { case Duration.Day: return dateTime.AddDays(1); case Duration.Week: return dateTime.AddDays(7); case Duration.Month: return dateTime.AddMonths(1); default: throw new ArgumentOutOfRangeException("duration"); } } } 

I think enums are evil, but at least extension methods can get rid of some conditional checks (switch / if) in your code. Remember to check that the enumeration value is in the correct range.

6. The order in which you describe static variables is important.


Some people claim that the variables are arranged in alphabetical order, and that there are special tools that can change their order for you. However, there is a scenario in which changing the order can break your application.

 static class Program { private static int a = 5; private static int b = a; static void Main(string[] args) { Console.WriteLine(b); } } 

This code will print the number 5. If you swap the description of the variables a and b, it will print 0.

7. Private class instance variable may be available in another instance of this class.


You might think that the following code will not work:

 class KeepSecret { private int someSecret; public bool Equals(KeepSecret other) { return other.someSecret == someSecret; } } 

It is easy to think that private means only this instance of a class, it can have access to it, but in reality this means that only this class has access to it ... including other instances of this class. In fact, it is very useful when implementing some comparison methods.

8. C # language specification already on your computer


If you have Visual Studio installed, you can find the specification in the VC # \ Specifications folder. VS 2011 comes with a C # 5.0 language specification in Word format.

It is full of many interesting facts such as:


And to those of you who say, “I knew all / most of this,” I say: “Where are you when I hire developers?”. Seriously, it's hard enough to find a C # developer with good language skills.

From myself I will add a couple of facts:

9. You cannot use a constant named value__ in the enumeration


I think this fact is known to many developers. In order to understand why it is so enough to know what the compilation is compiled into.

The following listing:

 public enum Language { C, Pascal, VisualBasic, Haskell } 

the compiler sees something like this:

 public struct Language : Enum { // ,      public const Language C = (Language)0; public const Language Pascal = (Language)1; public const Language VisualBasic = (Language)2; public const Language Haskell = (Language)3; //      Language //        public Int32 value__; } 

Now it is clear that the variable name value__ is simply reserved for storing the current value of the enumeration.

10. Field Initializer Allows Too Much


We all know how the field initializer works.

 class ClassA { public string name = "Tim"; public int age = 20; public ClassA() { } public ClassA(string name, int age) { this.name = name; this.age = age; } } 

The initialization code is simply placed in each of the constructors.

 class ClassA { public string name; public int age; public ClassA() { this.name = "Tim"; this.age = 20; base..ctor(); } public ClassA(string name, int age) { this.name = "Tim"; this.age = 20; base..ctor(); this.name = name; this.age = age; } } 

But if one constructor calls another, then the initialization is placed only in the constructor being called ...

 class ClassA { public string name = "Tim"; public int age = 20; public ClassA() { } public ClassA(string name, int age):this() { this.name = name; this.age = age; } } 

which is tantamount to

 class ClassA { public string name; public int age; public ClassA() { this.name = "Tim"; this.age = 20; base..ctor(); } public ClassA(string name, int age) { this..ctor(); this.name = name; this.age = age; } } 

And now the question is, what will happen if designers call each other? Where does the compiler decide to put the initialization code?

Answer: the compiler decides that once the first constructor calls the second, I will not put it into the first initialization. He will draw similar conclusions with the second designer. As a result, the initialization code will not be placed in any constructor ... And since it does not need to be placed anywhere, then why check it for errors? Thus, in the initialization code, you can write anything. For example,

 class ClassA { public string name = 123456789; public int age = string.Empty; public ClassA():this("Alan", 25) { } public ClassA(string name, int age):this() { this.name = name; this.age = age; } } 

Such code will compile without problems, however it will crash with stack overflow when creating an instance of this class.

11. You can use only C # synonyms when describing the basic type of enumeration.


As is known when describing an enumeration, we can explicitly indicate the base type underlying it. The default is int.

We can write like this

 public enum Language : int { C, Pascal, VisualBasic, Haskell } 

however we cannot write like that

 public enum Language : Int32 { C, Pascal, VisualBasic, Haskell } 

The compiler requires exactly C # synonymous type.

12. Structures are immutable when used in collections.


As you know, structures in .NET are significant types, that is, they are passed by value (copied), which is why changeable structures are evil. However, the fact that changeable structures are evil does not mean that they cannot be changed. By default, structures are mutable, that is, their state can be changed. But if structures are used in collections, they are no longer editable. Consider the code:

 struct Point { public int x; public int y; } static void Main(string[] args) { var p = new Point(); px = 5; py = 9; var list = new List<Point>(10); list.Add(p); list[0].x = 90;//  var array = new Point[10]; array[0] = p; array[0].x = 90;//  } 

In the first case, we get a compilation error, since the collection indexer is just a method that returns a copy of our structure. In the second case, we will not get an error, since indexing in arrays is not a method call, but an appeal to the desired element.

Thanks for reading.
I hope the article was helpful.

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


All Articles