はじめに
自分のブログにアップするつもりで、この記事を書いたのですが、せっかくなので、アドベントカレンダーに参加したいと思います。
でも、C#のアドベントカレンダーはすでにいっぱいです。それで、色々と調べていたら「東京理科大学 Advent Calendar 2017」というのがあるではないですか。
すでに、理工学部・情報科学科を卒業してから、30年以上の歳月が経ってしまいましたが、OB,OG可ということなので、空いている12月4日の記事としてアップさせていただくことにしました。
これまでアップされている記事を見ると、C#という言語がかなり異端っぽい気がしますが、そこは許してください。
元ネタ
ここで取り上げられている問題を引用してみると
ソースコードにダブルクォート、シングルクォート、数字を書くこと無く、「LIFULL」と出力して下さい。
最後に改行を付けること。
言語は問いません。
ということです。
C#でもやってみよう!
「ダブルクォート、シングルクォート、数字を書くこと無く」というのがいやらしいですね。
これをC#でいくつかのやり方で解いてみました。ただ、「LIFULL」ではなく、出力する文字列は、「HelloWorld」に変更します。
結構、いろんなやり方がありました。
nameof演算子を使うやり方
たぶん、これが一番簡単な方法だと思います。
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プロパティを使う方法でしょうか。
using System;
class HelloWorld {
static void Main(string[] args) {
Console.WriteLine(typeof(HelloWorld).Name);
}
}
これも、クラス名を HelloWorldにしています。これならば、C#5.0以前でもOKです。
リフレクションを使う (2)
もう一つ、リフレクションを使う例です。
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属性を使ってもできますね。
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だと動きません。
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属性は、その存在は知っていましたが、ほとんど使ったことがなかったので、勉強になりました。