IValueConverterの実装は型付きのBaseを作っておくと便利

C# 7.0から使えるようになった型スイッチを使用して、ちょっとだけ型安全なIValueConverterの実装を手軽にできるようにしてみる。

## 変換前後の型をつけたValueConverterBase

### Convert関数

IValueConverterのConvert関数を実装する際に、switchを利用して型に一致した場合だけ型付きのConvert関数に処理を渡すようにする。配列にも対応しておくと便利。

```csharp
// 継承クラスで実装する型Tから型UへのConvert関数
public abstract U Convert(T value, Type targetType, object parameter, string language);

// IValueConverterの実装
public object Convert(object value, Type targetType, object parameter, string language)
{
   switch (value)
    {
        case T t_val:
            return Convert(t_val, targetType, parameter, language);
        case IEnumerable<T> t_arr:
            return t_arr.Select(t => Convert(t, targetType, parameter,language));
        default:
            return null;
    }
}
```

### ConvertBack関数

逆変換の関数もConvert関数と同じくswitchを利用する。

```csharp
// 継承クラスで実装する型Uから型TへのConvertBack関数
public abstract T ConvertBack(U value, Type targetType, object parameter, string language);

// IValueConverterの実装
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
    switch (value)
    {
        case U u_val:
            return ConvertBack(u_val, targetType, parameter, language);
        case IEnumerable<U> u_arr:
            return u_arr.Select(u => ConvertBack(u, targetType, parameter, language));
        default:
            return null;
    }
}
```


### ValueConverterBaseクラス全体

```csharp
using System;
using System.Linq;
using System.Collections.Generic;

// TからUへの変換
public abstract class ValueConverterBase<T, U> : Windows.UI.Xaml.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        switch (value)
        {
            case T t_val:
                return Convert(t_val, targetType, parameter, language);
            case IEnumerable<T> t_arr:
                return t_arr.Select(t => Convert(t, targetType, parameter, language));
            default:
                return null;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        switch (value)
        {
            case U u_val:
                return ConvertBack(u_val, targetType, parameter, language);
            case IEnumerable<U> u_arr:
                return u_arr.Select(u => ConvertBack(u, targetType, parameter, language));
            default:
                return null;
        }
    }

    public abstract U Convert(T value, Type targetType, object parameter, string language);
    public abstract T ConvertBack(U value, Type targetType, object parameter, string language);
}
```

あと、parameterを型にしてもよいかもしれない。


## 使い方

### BoolからVisibilityにするよくあるやつ

ヒントから「考えられる修正」で型付きのConvert関数をすぐ実装できるのが地味に便利。

![boolvisibility.png](https://qiita-image-store.s3.amazonaws.com/0/106819/206cc8e3-5b0f-31ed-1310-10a0de18f2d2.png)

自動生成されるthrow NotImplementExceptionを削除し、型にあわせてコードを書けば簡単にConverterの中身を実装できる。

```csharp
using Windows.UI.Xaml;

public class BoolVisibilityConverter : ValueConverterBase<bool, Visibility>
{
    public override Visibility Convert(bool value, Type targetType, object parameter, string language)
    {
        return value ? Visibility.Visible : Visibility.Collapsed;
    }

    public override bool ConvertBack(Visibility value, Type targetType, object parameter, string language)
    {
        return value == Visibility.Visible;
    }
}
```

## WPFの場合

```csharp
using System;
using System.Globalization;
using System.Linq;
using System.Collections.Generic;

public abstract class ValueConverterBase<T, U> : System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        switch (value)
        {
            case T t_val:
                return Convert(t_val, targetType, parameter, culture);
            case IEnumerable<T> t_arr:
                return t_arr.Select(t => Convert(t, targetType, parameter, culture));
            default:
                return null;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        switch (value)
        {
            case U u_val:
                return ConvertBack(u_val, targetType, parameter, culture);
            case IEnumerable<U> u_arr:
                return u_arr.Select(u => ConvertBack(u, targetType, parameter, culture));
            default:
                return null;
        }
    }

    public abstract U Convert(T value, Type targetType, object parameter, CultureInfo culture);
    public abstract T ConvertBack(U value, Type targetType, object parameter, CultureInfo culture);
}
```