「強いエンジニアにHelloWorldさせてみた(縛りあり)」をC#でやってみた


## はじめに

自分のブログにアップするつもりで、この記事を書いたのですが、せっかくなので、アドベントカレンダーに参加したいと思います。

でも、C#のアドベントカレンダーはすでにいっぱいです。それで、色々と調べていたら「[東京理科大学 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/tokyo_university_of_science)」というのがあるではないですか。
すでに、理工学部・情報科学科を卒業してから、30年以上の歳月が経ってしまいましたが、OB,OG可ということなので、空いている12月4日の記事としてアップさせていただくことにしました。

これまでアップされている記事を見ると、C#という言語がかなり異端っぽい気がしますが、そこは許してください。



## 元ネタ

[強いエンジニアにHelloWorldさせてみた(縛りあり)](https://qiita.com/imishinist/items/1883b5eef836aeee93ff)

ここで取り上げられている問題を引用してみると

> ソースコードにダブルクォート、シングルクォート、数字を書くこと無く、「LIFULL」と出力して下さい。
> 最後に改行を付けること。
> 言語は問いません。

ということです。

## C#でもやってみよう！

「ダブルクォート、シングルクォート、数字を書くこと無く」というのがいやらしいですね。

これをC#でいくつかのやり方で解いてみました。ただ、「LIFULL」ではなく、出力する文字列は、「HelloWorld」に変更します。

結構、いろんなやり方がありました。


## nameof演算子を使うやり方

たぶん、これが一番簡単な方法だと思います。


```csharp
using System;

class HelloWorld {
    static void Main(string[] args) {
        Console.WriteLine(nameof(HelloWorld));
    }
}
```

classの名前を'HelloWorld'にして、そのクラス名をnameof演算子で取り出しています。
nameof演算子は、C#6.0以降で利用できる演算子です。なので、C#5.0以前のバージョンでは利用できません。

## リフレクションを使う (1)

次に簡単だと思われるとが、TypeクラスのNameプロパティを使う方法でしょうか。


```csharp
using System;

class HelloWorld {
    static void Main(string[] args) {
        Console.WriteLine(typeof(HelloWorld).Name);
    }
}
```

これも、クラス名を HelloWorldにしています。これならば、C#5.0以前でもOKです。

## リフレクションを使う (2)
                                              
もう一つ、リフレクションを使う例です。

```csharp
using System;
using System.Linq;
using System.Reflection;

class Program {
    public static string HelloWorld { get; set; }
    static void Main(string[] args) {
        var n = typeof(Program).GetProperties(BindingFlags.Public | BindingFlags.Static).First().Name;
        Console.WriteLine(n);
    }
}
```    

これは、クラス名ではなく、プロパティ名をリフレクション使って取得しています。GetPropertiesメソッドの戻り値は配列なので、先頭の要素を取得するのに、LINQのFirstメソッドを使っています。[0]は数字使うのでアウトです。

## CallerMemberName属性を使う

C#5.0で導入されたCallerMemberName属性を使ってもできますね。

```csharp
using System;
using System.Runtime.CompilerServices;

class Program {

    static void Main(string[] args) {
        HelloWorld();
    }

    static void HelloWorld() {
        Print();
    }

    static void Print([CallerMemberName] string member = null) {
        Console.WriteLine(member);
    }
}
```


だんだん、複雑なコードになってきました。CallerMemberName属性使うと、呼び出し元のメソッド名を引数として受け取れるので、HelloWorldというメソッドを定義し、そこからPrintメソッドを呼び出すことで、Printメソッドの中で、文字列"HelloWorld" を取得できるというわけです。

## リフレクション使わずに強引にやる

最後は、本当に強引な方法です。

System.Windows.Forms名前空間に定義されているKeys列挙型を使って、”HelloWorld”を組み立てています。
途中LINQ使ってますが、LINQは必須ではないので、書き換えれば、C#2.0でも動くプログラムにすることができると思います。

""も使えないので、String.Empty使ってます。

なお、他のコードは、.NET Coreでも動作しますが、このコードだけは、.NET Coreだと動きません。
                                                                                
```csharp
using System;
using System.Linq;
using System.Windows.Forms;

class Program {
    static void Main(string[] args) {
    
        string CreateString(Keys[] keys) {
            int index = String.Empty.Length;　// 0をセット
            return keys.Aggregate(String.Empty, (s, k) => { 
            　　return s + (index++ == String.Empty.Length ? k.ToString() : k.ToString().ToLower()); 
            });
        }
        
        var hello = new Keys[] { Keys.H, Keys.E, Keys.L, Keys.L, Keys.O };
        Console.Write(CreateString(hello));
        var world = new Keys[] { Keys.W, Keys.O, Keys.R, Keys.L, Keys.D };
        Console.WriteLine(CreateString(world));
    }
}
```

## 最後に

結構、いろんなやり方がありましたね。こういった縛りのある問題をとくと、言語の持つ機能を理解するのに大いに役立ちますね。
CallerMemberName属性は、その存在は知っていましたが、ほとんど使ったことがなかったので、勉強になりました。