3

NHibernate がカスタム型から文字列に、またはその逆に変換できるようにするインターフェイスを構築しています。

public interface IStringToTypeConverter<T>
{
    T FromString(string value);
    string ToString(T value);
}

タイプTは何でもかまいません。この例では、列挙型を使用します。NHibernate には列挙型を変換する機能があることは知っていますが、これは問題の最も単純な例です。

public enum TransactionStatus { Failed, Pending, Succeeded }

public class TransactionStatusConverter : IStringToTypeConverter<TransactionStatus>
{
    public TransactionStatus FromString(string value)
    {
        return (TransactionStatus)Enum.Parse(typeof (TransactionStatus), value, true);
    }

    public string ToString(TransactionStatus value)
    {
        return value.ToString();
    }
}

これまでのところは問題ありませんTransactionStatusConverterが、ジェネリック型と制約を持つクラスにジェネリック型として指定しようとすると、問題が発生しますIStringToTypeConverter<T>

public sealed class CustomStringType<TConverter, TType> : IUserType where TConverter : IStringToTypeConverter<TType>, new()
{
    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var value = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;
        var converter = new TConverter();
        return converter.FromString(value);
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var converter = new TConverter();
        ((IDataParameter)cmd.Parameters[index]).Value
            = converter.ToString((TType)value);
    }
}

NHibernate でのこのクラスの使用法:

Property(x => x.Status, map =>
{
    map.Column("TransactionStatusID");
    map.Type<CustomStringType<TransactionStatusConverter, TransactionStatus>>();
}); 

Type<>()関数の型制約は ですIUserType

私の問題は、コンバーターのタイプから推測する必要があるため、冗長でmap.Type<CustomStringType<TransactionStatusConverter, TransactionStatus>>();あると感じることです。TransactionStatus

CustomStringType私のマッピングコードが次のように読めるようにするにはどうすればよいでしょうmap.Type<CustomStringType<TransactionStatusConverter>>();か?

4

2 に答える 2

2

IStringToTypeConverter<Foo>型パラメーターを省略できたとしても、 と の両方を実装した単一のクラスを作成することを妨げるものは何もありませんIStringToTypeConverter<Bar>。そのクラスが両方を行った場合、どの型を 2 番目の型パラメーターとして使用する必要があると安全に推測できますCustomStringTypeか? 覚えておいてください: コンパイラが作成できる論理的な仮定のみを行うことができます。ごまかしたり、ドメインの知識を使用して問題を解決したりすることはできません。

一方、ひっくり返してTransactionStatus型だけを提供し、リフレクションが何らかの形で問題を解決することをひそかに望んでいる場合、2 つの異なるクラスが両方とも を実装する可能性がありますIStringToTypeConverter<TransactionStatus>。あなたはまだ問題を抱えており、あなたの問題に対してどれが正しいかわかりません。繰り返しになりますが、ドメインの知識がなければ、この問題はコンパイラにとって扱いにくいものです。

于 2013-07-30T18:10:57.873 に答える
1

C# は、ジェネリック型パラメーターの部分的な推論をサポートしていません。

1 つの方法は、using エイリアス ディレクティブを使用することです。

using CustomStringTypeTransactionStatusConverter = CustomStringType<TransactionStatusConverter, TransactionStatus>;

次に、次のようにできます。

map.Type<CustomStringTypeTransactionStatusConverter>();

上記のアプローチは、サブクラスを定義するよりも優れていることに注意してください。これは、基本クラスとサブクラスが異なる型であるためです (これは、意味的に同じ型であると見なし、メソッドパラメーターなどでそれらをそのまま使用したい場合には不適切です. ):

// This is bad because it defines a new type
public class SubClass : CustomStringType<TransactionStatusConverter, TransactionStatus>
{
}

ただし、特定のケースでは、簡単なリファクタリングを行うことができます (コードでは TType のみを使用してオブジェクトをキャストし、このキャストは TransactionStatusConverter 自体で実行できることに注意してください)。

最初に追加のインターフェースを導入します。

public interface IStringToTypeConverter<T> : IStringToTypeConverterUntyped
{
    T FromString(string value);
    string ToString(T value);
}

public interface IStringToTypeConverterUntyped
{
    object FromStringUntyped(string value);
    string ToString(object value);
}

メンバーを実装します。

public class TransactionStatusConverter : IStringToTypeConverter<TransactionStatus>
{
    public TransactionStatus FromString(string value)
    {
        return (TransactionStatus)Enum.Parse(typeof(TransactionStatus), value, true);
    }

    public object FromStringUntyped(string value)
    {
        return FromString(value);
    }

    public string ToString(TransactionStatus value)
    {
        return value.ToString();
    }

    public string ToString(object value)
    {
        return ToString((TransactionStatus)value);
    }
}

CustomStringType を変更します。

public sealed class CustomStringType<TConverter>
    where TConverter : IStringToTypeConverterUntyped, new()
{
}

これで、次のように使用できます。

map.Type<CustomStringType<TransactionStatusConverter>>();

追加の注意: 多くのコンバーターがあり、各コンバーター (TransactionStatusConverter など) に IStringToTypeConverterUntyped を実装したくない場合は、IStringToTypeConverterUntyped で宣言されたメンバーを実装し、抽象メンバーも宣言する基本クラス ConverterBase からすべてのコンバーターを継承させることができます。 IStringToTypeConverter< T > で宣言されたメンバー。

于 2013-07-31T10:56:27.703 に答える