Ctrl + カーソルキーにてページ移動します
2010/08/xx onozaty

第37回 社内勉強会

JavaからC#へ

C#とは

JavaとC#

JavaからC#へ

Hello World

Java C#
package helloworld;

import java.lang.System;

public class Hello {

    public static void main(String[] args) {

        System.out.println("Hello World!");
    }
}
using System;

namespace HelloWorld
{
    public class Hello
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

プロパティ

Java C#
public class User {
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
user.setId(1);
user.setName("山田 太郎");

System.out.format(
    "id:%s name:%s", user.getId(), user.getName());
public class User {
    private int id;
    public int Id
    {
        // get, setアクセサ
        get
        {
            return id;
        }
        set
        {
            id = value;
        }
    }

    // 省略形式
    public string Name { get; set; }
}
// まるでフィールドのようにアクセス
user.Id = 1;
user.Name = "山田 太郎";

Console.WriteLine("id:{0} name:{1}", user.Id, user.Name);

オブジェクト初期化子、コレクション初期化子

C#
class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void Main()
{
    // オブジェクト初期化子を使わない場合
    User user1 = new User();
    user1.Id = 1;
    user1.Age = 22;

    // オブジェクト初期化子によって1つのステートメントとして
    // プロパティの代入までできる
    User user2 = new User
    {
        Id = 2,
        Age = 34,
    };

    // コレクション初期化
    List<int> ids = new List<int> { 1, 2, 3 };
    Dictionary<int, string> dic = new Dictionary<int, string>
    {
        {1, "aaa"},
        {2, "bbb"}
    };
}

const, readonly

Java C#
public class Hoge {

    public static final int MAX = 10;

    private final int num;

    public Hoge(int num) {
        this.num = num;
    }
}
public class Hoge
{
    public const int MAX = 10;

    private readonly int num;

    public Hoge(int num)
    {
        this.num = num;
    }
}

リテラル文字列(ヒアドキュメント)

C#
string path1 = "\\\\FileShare\\Directory\\file.txt";

// エスケープシーケンスが不要となる
string path2 = @"\\FileShare\Directory\file.txt";

// 行をまたいでもOK
string multiLine = @"1行目
2行目
3行目";

switch

C#
static void Main(string[] args)
{
    switch (args[0])
    {
        case "copy":
            // ...
            break;
        case "move":
            // ...
            // 別のcaseの処理を行わせる場合には
            // 明示的に指定する必要あり
            // (goto, breakのどちらかがないとコンパイルエラー)
            goto case "delete";
        case "del":
        case "delete":
            // ...
            break;
        default:
            // ...
            break;
    }
}

引数の参照渡し(ref,out)

C#
static void Test()
{
    int a = 1, b = 2;
    Swap(ref a, ref b);
    Console.WriteLine("a:{0} b:{1}", a, b); // a:2 b:1

    // outの場合、呼び出し元での初期化は不要
    int c;
    TestOut(out c);
    Console.WriteLine(("c:{0}", c); // c:1
}

static void Swap(ref int a, ref int b)
{
    // 入れ替え
    int temp = a;
    a = b;
    b = temp;
}

static void TestOut(out int a)
{
    a = 1;
}

演算子オーバロード

C#
public class OperatorTest
{
    public string Text { get; private set; }
    public int Num { get; private set; }

    public OperatorTest(string text, int num)
    {
        Text = text;
        Num = num;
    }

    // 戻り値の型 operator 演算子 (引数リスト) で定義
    // 下記は +演算子をオーバロード
    public static OperatorTest operator +(OperatorTest o1, OperatorTest o2)
    {
        // Text,Numをそれぞれ足したオブジェクトを生成して返却
        return new OperatorTest(o1.Text + o2.Text, o1.Num + o2.Num);
    }
}

OperatorTest test1 = new OperatorTest("test1", 1);
OperatorTest test2 = new OperatorTest("test2", 2);

OperatorTest test3 = test1 + test2;

Console.WriteLine("Text:{0} Num:{1}", test3.Text, test3.Num); // Text:test1test2 Num:3

インデクサ

C#
class Indexer
{
    Dictionary<int, String> innerDic = new Dictionary<int, string>();

    // this[インデックスとなる引数] で定義
    public string this[int i]
    {
        get { return innerDic[i]; }
        set { innerDic[i] = value; }
    }

    // インデックスは数値以外でもOK
    public string this[string s]
    {
        get { return this[int.Parse(s)]; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Indexer indexer = new Indexer();

        indexer[1] = "hoge";
        // 配列、Dictionaryのようにアクセス
        Console.WriteLine(indexer[1]); // hoge
        Console.WriteLine(indexer["1"]); // hoge
    }
}

反復子、yield

C#
// 反復子ブロックはIEnumerableを戻り値とする
// (IEnubrable を実装するクラスを自動生成)
public static IEnumerable<int> Range(int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        // yelid return が呼ばれるたびに、値を1つ返す
        yield return i;
    }
}
public static IEnumerable Nums()
{
    // for文など使わずに、そのまま書いても良い
    yield return 1;
    yield return 2;
    yield return 3;
}
public static void Main2()
{
    foreach (int num in Range(1, 6))
    {
        Console.Write("{0} ", num); // 1 2 3 4 5 6
    }
}

例外

Java C#
public static void main(String[] args) throws Exception {

    try {
        throwException();
    } catch (FileNotFoundException fe) {
        //
    } catch (IOException ie) {
        throw ie; // 例外をそのまま上位へ
    } catch (Exception e) {
        // まとめてcatch
    } finally {

    }
}

public static void throwException() throws FileNotFoundException,
        IOException {
}
static void Main(string[] args)
{
    try
    {
        ThrowException();
    }
    catch (FileNotFoundException e)
    {
        throw e; // スタックトレースが再設定(この位置からのスタックになる)
    }
    catch (IOException) // 例外を参照しないなら、例外クラス名だけでOK
    {
        throw; // スタックトレースはそのまま
    }
    catch
    {
        // 全ての例外(例外を参照しない場合)
        // 参照する場合は、Java同様 catch (Exception e) と書ける
    }
    finally
    {
    }
}

static void ThrowException() // throws句は不要
{
    throw new FileNotFoundException();
}

usingによるリソース開放

Java C#
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream("memo.txt"), "UTF-8"));
try {
    writer.write("テキスト");
} finally {
    writer.close();
}
// usingで宣言した変数に対するDisposeメソッド(リソース解放)が
// ブロックを抜ける際に実行される
using (StreamWriter writer = 
                new StreamWriter("memo.txt", false, Encoding.UTF8))
{
    writer.Write("テキスト");
}

null許容型、null合体演算子

C#
// 値型に?をつけるとnullを代入可能な変数となる
int? i = 10;
i = null;

bool? b = null;

// null合体演算子(??演算子)で、値がnullかチェックし、
// nullの場合、代替値を返す
bool b2 = b ?? false; // bがnull以外の場合はb、bがnullの場合falseが代入される

// null合体演算子は、参照型でも使える
string str = null;
string notNullStr = str ?? string.Empty; // strがnullの場合、空文字

delegate

Java C#
public class DelegateTest {

    public void execute(IExecuteCallback callback) {
        callback.executeCallback(new Result());
    }
    public static void main(String[] args) {
        // Javaだと関数をそのまま渡すことはできない(クラスありき)
        DelegateTest caller = new DelegateTest();
        caller.execute(new ExecuteCallbaclImpl());

        // 無名インナークラスが良く使われる
        caller.execute(new IExecuteCallback() {
            public void executeCallback(Object result) {
                System.out.println(result);
            }
        });
    }
}

interface IExecuteCallback {
    void executeCallback(Object result);
}

class ExecuteCallbaclImpl implements IExecuteCallback {
    public void executeCallback(Object result) {
        System.out.println(result);
    }
}
class DelegateTest
{
    // object型の引数を受け取るdelegate定義
    public delegate void ExecuteCallback(object result);

    // delegateを引数として受け取るメソッド
    public void Execute(ExecuteCallback callback)
    {
        callback(new Result()); // delegateを呼び出し
    }
    public static void Main(string[] args)
    {
        DelegateTest caller = new DelegateTest();

        // delegate宣言したものとシグネチャが一致していれば
        // そのメソッドに対するdelegateを生成できる
        ExecuteCallback callback = new ExecuteCallback(Callback);
        caller.Execute(callback);
        caller.Execute(Callback); // new ExecuteCallbackは省略可能

        // 匿名メソッドも指定可能(delegateキーワードは、匿名メソッド宣言でも使用)
        caller.Execute(delegate(object result)
        {
            Console.WriteLine(result);
        });
    }
    public static void Callback(object result)
    {
        Console.WriteLine(result);
    }
}

ラムダ

C#
// delegateによる匿名メソッド
Func<int, int> square1 = delegate(int x)
{
    return x * x;
};

// ラムダ式によってさらに短く(引数リスト => 式)
Func<int, int> square2 = (int x) => x * x;
Func<int, int> square3 = x => x * x; // 引数の型も省略(コンパイラにより型推定)

// ステートメント形式のラムダ
// (複数行かけるが、この場合はreturnが必要)
Func<int, int> square4 = x => {
    return x * x;
};

List<int> nums = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
// 3以下のものを取得
List<int> result = nums.FindAll(x => x < 3);

イベント

C#
// 今回のイベントにて使うdelegate
public delegate void SampleEventHandler(object sender, DateTime time);

class EventSample
{
    // イベント処理用のdelegate変数
    public event SampleEventHandler CalledTimer;

    public void FireEvent()
    {
        // イベント起動
        if (CalledTimer != null) CalledTimer(this, DateTime.Now);
    }

    public static void Main2()
    {
        EventSample sample = new EventSample();

        // イベントの登録は、+=演算子で行う
        sample.CalledTimer += new SampleEventHandler(delegate(object sender, DateTime time)
        {
            Console.WriteLine("call1 " + time);
        });
        sample.CalledTimer += new SampleEventHandler(Sample_CalledTimer);

        sample.FireEvent();

        // イベントの解除は、-=演算子で行う
        sample.CalledTimer -= new SampleEventHandler(Sample_CalledTimer);
    }

    static void Sample_CalledTimer(object sender, DateTime time)
    {
        Console.WriteLine("Sample_CalledTimer " + time);
    }
}

LINQ

C#
List<User> users = new List<User>()
{
    new User { Id = 3, Name = "Smith", Age = 19 },
    new User { Id = 1, Name = "Joe", Age = 20 },
    new User { Id = 2, Name = "Andy", Age = 32 },
    new User { Id = 4, Name = "Judy", Age = 50 },
};

// ListからAgeが20から40の間のオブジェクトのIdを取得(Idの昇順にて)
var ids = from user in users
          where user.Age >= 20 && user.Age <= 40
          orderby user.Id
          select user.Id;

拡張メソッド

C#
static class Extensions
{
    // staticメソッドでパラメータの前にthisを付与
    public static int WordCount(this string str)
    {
        // スペース、カンマ区切りで分割した単語数
        return str.Split(new char[] { ' ', ',' },
                         StringSplitOptions.RemoveEmptyEntries).Length;
    }

    static void Main(string[] args)
    {
        int count1 = Extensions.WordCount("Hello Word");

        // インスタンスメソッドのように呼び出せる
        int count2 = "Hello Word".WordCount();
    }
}

型推論

C#
// コンパイラによってint型の宣言と判断される
var i = 1;

// 下記はコンパイルエラー(iはint型)
// i = "a";

// 変数の定義が短くできる
Dictionary<string, string> dictionary1 = new Dictionary<string, string>();
var dictionary2 = new Dictionary<string, string>();

// このような使い方すると、resultの型がわかりずらくなる
var result = Process(1);

匿名型

C#
// 匿名型として宣言(コンパイラによって、自動的に型生成)
var x = new { Id = 1, Name = "AAA" };

Console.WriteLine("Id:{0} Name:{1}", x.Id, x.Name);

List<User> users = new List<User>()
{
    new User { Id = 3, Name = "Smith", Age = 19 },
    new User { Id = 1, Name = "Joe", Age = 20 },
    new User { Id = 2, Name = "Andy", Age = 32 },
    new User { Id = 4, Name = "Judy", Age = 50 },
};

// プロパティ名を省略すると、初期化時に使われたプロパティ名になる
var hoge = new { users[0].Id, users[0].Name };
Console.WriteLine("Id:{0} Name:{1}", hoge.Id, hoge.Name);

// 匿名型は主にLINQで使う
var ids = from user in users
          where user.Age >= 20 && user.Age <= 40
          orderby user.Id
          select new { user.Id, user.Name }; // 結果として取得するフィールドを絞る

List操作

C#
List<User> users = new List<User>()
{
    new User { Id = 3, Name = "Smith", Age = 19 },
    new User { Id = 1, Name = "Joe", Age = 20 },
    new User { Id = 2, Name = "Andy", Age = 32 },
    new User { Id = 4, Name = "Judy", Age = 50 },
};

// 一致する最初の要素を返す
users.Find(x => x.Age > 30); // Id = 2

// 上記をforeachで
User findUser = null;
foreach (User user in users)
{
    if (user.Age > 30)
    {
        findUser = user;
        break;
    }
}

// 一致する全ての要素を返す
users.FindAll(x => x.Age > 30); // Id = 2, 4

// 条件に一致するものがあるか
users.Exists(x => x.Name == "Andy"); // true

// 別の型に変換
users.ConvertAll(x => x.Id); // List<int> {3, 1, 2, 4}

条件付きコンパイル

C#
static void Main(string[] args)
{
    Console.WriteLine("Main");
#if DEBUG
    Console.WriteLine("デバッグビルド時のみ");
#endif
}

オプション引数、名前付き引数(C# 4.0)

C#
static void Main(string[] args)
{
    // オプション引数により引数を省略可能
    Calc();
    Calc(1);
    Calc(1, 2);
    Calc(1, 2, 3);

    // 名前付き引数により、引数の順序を自由に書ける
    // (任意の箇所を省略可能になる)
    Calc(c: 1);
    Calc(c: 1, b: 2, a: 3);
}

static int Calc(int a = 0, int b = 0, int c = 0)
{
    return a + b + c;
}

dynamic(C# 4.0)

C#
class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

static void Main(string[] args)
{
    Calc(new Point { X = 1, Y = 2 });
    Calc(new { X = 1, Y = 2 });
    Calc(new { X = 1 }); // Yがないので実行時にエラー
}

static int Calc(dynamic obj)
{
    // dynamic型では、静的な型チェックが行われない
    // (実行時にチェック)
    return obj.X + obj.Y;
}

他にも

おわりに