私はCEZARYPIĄTEKによる記事The Magical Methods in C#の翻訳をあなたの注意に提示します。
C#には、言語をサポートする特定のメソッドシグネチャセットがあります。このようなシグネチャを持つメソッドを使用すると、すべての利点を備えたカスタム構文を使用できます。たとえば、コードを簡素化したり、DSLを作成したりして、問題の解決策をより美しく表現することができます。私はどこでもそのような方法に出会うので、私は投稿を書いて、このトピックに関する私のすべての発見を要約することを決めました。
コレクションの初期化構文
コレクションの初期化構文は、 C#3.0(2007年末にリリース)に存在するため、かなり古い機能です。コレクションの初期化構文を使用すると、1つのブロックに要素を含むリストを作成できることを思い出してください。
var list = new List<int> { 1, 2, 3 };
このコードは次と同等です。
var temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
var list = temp;
BCL. , :
-
IEnumerable
-
void Add(T item)
public class CustomList<T>: IEnumerable
{
public IEnumerator GetEnumerator() => throw new NotImplementedException();
public void Add(T item) => throw new NotImplementedException();
}
, Add
:
public static class ExistingTypeExtensions
{
public static void Add<T>(this ExistingType @this, T item) => throw new NotImplementedException();
}
- :
class CustomType
{
public List<string> CollectionField { get; private set; } = new List<string>();
}
class Program
{
static void Main(string[] args)
{
var obj = new CustomType
{
CollectionField =
{
"item1",
"item2"
}
};
}
}
. ? :
var obj = new CustomType
{
CollectionField =
{
{ existingItems }
}
};
, :
-
IEnumerable
-
void Add(IEnumerable<T> items)
public class CustomList<T>: IEnumerable
{
public IEnumerator GetEnumerator() => throw new NotImplementedException();
public void Add(IEnumerable<T> items) => throw new NotImplementedException();
}
, BCL void Add(IEnumerable<T> items)
, , :
public static class ListExtensions
{
public static void Add<T>(this List<T> @this, IEnumerable<T> items) => @this.AddRange(items);
}
:
var obj = new CustomType
{
CollectionField =
{
{ existingItems.Where(x => /*Filter items*/).Select(x => /*Map items*/) }
}
};
(IEnumerable):
var obj = new CustomType
{
CollectionField =
{
individualElement1,
individualElement2,
{ list1.Where(x => /*Filter items*/).Select(x => /*Map items*/) },
{ list2.Where(x => /*Filter items*/).Select(x => /*Map items*/) },
}
};
.
, -, protobuf
. , protobuf
: grpctools .NET proto
, - :
[DebuggerNonUserCode]
public RepeatableField<ItemType> SomeCollectionField
{
get
{
return this.someCollectionField_;
}
}
, - , RepeatableField
void Add(IEnumerable items)
, - :
/// <summary>
/// Adds all of the specified values into this collection. This method is present to
/// allow repeated fields to be constructed from queries within collection initializers.
/// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>
/// method instead for clarity.
/// </summary>
/// <param name="values">The values to add to this collection.</param>
public void Add(IEnumerable<T> values)
{
AddRange(values);
}
var errorCodes = new Dictionary<int, string>
{
[404] = "Page not Found",
[302] = "Page moved, but left a forwarding address.",
[500] = "The web server can't come out to play today."
};
:
var errorCodes = new Dictionary<int, string>();
errorCodes[404] = "Page not Found";
errorCodes[302] = "Page moved, but left a forwarding address.";
errorCodes[500] = "The web server can't come out to play today.";
, .
— , Dictionary<T>
, :
class HttpHeaders
{
public string this[string key]
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
var headers = new HttpHeaders
{
["access-control-allow-origin"] = "*",
["cache-control"] = "max-age=315360000, public, immutable"
};
}
}
C# 7.0 . :
var point = (5, 7);
// decomposing tuple into separated variables
var (x, y) = point;
:
ValueTuple<int, int> point = new ValueTuple<int, int>(1, 4);
int x = point.Item1;
int y = point.Item2;
:
int x = 5, y = 7;
//switch
(x, y) = (y,x);
:
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
}
, . , :
-
Deconstruct
-
void
-
out
Point
:
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
:
var point = new Point(2, 4);
var (x, y) = point;
" " :
int x;
int y;
new Point(2, 4).Deconstruct(out x, out y);
:
public static class PointExtensions
{
public static void Deconstruct(this Point @this, out int x, out int y) => (x, y) = (@this.X, @this.Y);
}
— KeyValuePair<TKey, TValue>
, :
foreach (var (key, value) in new Dictionary<int, string> { [1] = "val1", [2] = "val2" })
{
//TODO: Do something
}
KeyValuePair<TKey, TValue>.Deconstruct(TKey, TValue)
netstandard2.1
. netstandard
.
awaitable
C# 5.0 ( Visual Studio 2012) async/await
, . , :
void DoSomething()
{
DoSomethingAsync().ContinueWith((task1) => {
if (task1.IsCompletedSuccessfully)
{
DoSomethingElse1Async(task1.Result).ContinueWith((task2) => {
if (task2.IsCompletedSuccessfully)
{
DoSomethingElse2Async(task2.Result).ContinueWith((task3) => {
//TODO: Do something
});
}
});
}
});
}
private Task<int> DoSomethingAsync() => throw new NotImplementedException();
private Task<int> DoSomethingElse1Async(int i) => throw new NotImplementedException();
private Task<int> DoSomethingElse2Async(int i) => throw new NotImplementedException();
async/await
:
async Task DoSomething()
{
var res1 = await DoSomethingAsync();
var res2 = await DoSomethingElse1Async(res1);
await DoSomethingElse2Async(res2);
}
, await
Task
. , GetAwaiter
, :
-
System.Runtime.CompilerServices.INotifyCompletion
void OnCompleted(Action continuation)
-
IsCompleted
-
GetResult
await
GetAwaiter
, TaskAwaiter<TResult>
, :
class CustomAwaitable
{
public CustomAwaiter GetAwaiter() => throw new NotImplementedException();
}
class CustomAwaiter: INotifyCompletion
{
public void OnCompleted(Action continuation) => throw new NotImplementedException();
public bool IsCompleted => throw new NotImplementedException();
public void GetResult() => throw new NotImplementedException();
}
: " await
awaitable ?". , Stephen Toub "await anything", .
query expression
C# 3.0 — Language-Integrated Query, LINQ, SQL- . LINQ : SQL- . , . . , . LINQ , SQL- , . . C#, CLR. LINQ IEnumerable
, IEnumerable<T>
IQuerable<T>
, , , query expression. , LINQ, :
class C
{
public C<T> Cast<T>();
}
class C<T> : C
{
public C<T> Where(Func<T,bool> predicate);
public C<U> Select<U>(Func<T,U> selector);
public C<V> SelectMany<U,V>(Func<T,C<U>> selector, Func<T,U,V> resultSelector);
public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector, Func<U,K> innerKeySelector, Func<T,U,V> resultSelector);
public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector, Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector);
public O<T> OrderBy<K>(Func<T,K> keySelector);
public O<T> OrderByDescending<K>(Func<T,K> keySelector);
public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector);
public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector, Func<T,E> elementSelector);
}
class O<T> : C<T>
{
public O<T> ThenBy<K>(Func<T,K> keySelector);
public O<T> ThenByDescending<K>(Func<T,K> keySelector);
}
class G<K,T> : C<T>
{
public K Key { get; }
}
, , LINQ
. LINQ
. , , Understand monads with LINQ Miłosz Piechocki.
この記事の目的は、これらの構文トリックを悪用するようにあなたを説得することではなく、それらをより明確にすることです。一方、常に回避できるわけではありません。これらは使用するように設計されており、コードを改善できる場合があります。結果のコードが同僚に理解できないことを恐れている場合は、知識を同僚と共有する方法(または少なくともこの記事へのリンク)を見つける必要があります。これがそのような「魔法の方法」の完全なセットであるかどうかはわかりませんので、ご存知の場合はコメント欄で共有してください。