文字列とジェネリック型を渡したい次のシナリオがあります。
_public class Worker {
public void DoSomeWork<T>(string value)
where T : struct, IComparable<T>, IEquatable<T> { ... }
}
_
途中のある時点で、文字列値をT
値に変換する必要があります。ただし、文字列をT
型に変換できない場合はロジックを実行する必要があるため、直接変換したくありません。
私はConvert.ChangeType()
を試してみようと思っていましたが、変換しないと例外がスローされ、DoSomeWork()
メソッドが頻繁に実行されるという問題があります。変換が有効かどうかを判断するには、try/catchを使用する必要があります。
だから私は考えさせられました、私は数値型を扱うことになるので、Tは次のいずれかになります:int
、uint
、short
、ushort
、long
、ulong
、byte
、sbyte
、decimal
、float
、double
。これを知って、数値型を使用することがわかっているという事実を処理することで、より高速な解決策を考え出すことができると考えました(T
が数値型でない場合は、例外をスローします)。 ..
_public class NumericWorker {
public void DoSomeWork<T>(string value)
where T : struct, IComparable<T>, IEquatable<T>
{
ParseDelegate<T> tryConverter =
SafeConvert.RetreiveNumericTryParseDelegate<T>();
...
}
}
public class SafeConvert
{
public delegate bool ParseDelegate<T>(string value, out T result);
public static ParseDelegate<T> RetreiveNumericTryParseDelegate<T>()
where T : struct, IComparable<T>, IEquatable<T>
{
ParseDelegate<T> tryParseDelegate = null;
if (typeof(T) == typeof(int))
{
tryParseDelegate = (string v, out T t) =>
{
int typedValue;
bool result = int.TryParse(v, out typedValue);
t = result ? (T)typedValue : default(T);
//(T)Convert.ChangeType(typedValue, typeof(T)) : default(T);
return result;
};
}
else if (typeof(T) == typeof(uint)) { ... }
else if (typeof(T) == typeof(short)) { ... }
else if (typeof(T) == typeof(ushort)) { ... }
else if (typeof(T) == typeof(long)) { ... }
else if (typeof(T) == typeof(ulong)) { ... }
else if (typeof(T) == typeof(byte)) { ... }
else if (typeof(T) == typeof(sbyte)) { ... }
else if (typeof(T) == typeof(decimal)) { ... }
else if (typeof(T) == typeof(float)) { ... }
else if (typeof(T) == typeof(double)) { ... }
return tryParseDelegate;
}
}
_
しかし、上記はtypedValue
をT
にキャストすると問題が発生し、回避できる唯一の方法であるため、t = result ? (T)typedValue : default(T);
を記述できないという問題があります。ここまでは_(T)Convert.ChangeType(typedValue, typeof(T))
_と書くことです。しかし、これを実行すると、別の変換を実行しているだけです。
したがって、私が誰かがこの問題を解決する方法を知っているかどうか(ChangeType()
を実行することが問題であると考える場合)、または私が考慮していないより良い解決策があるかどうか疑問に思いました。
t =結果? (T)typedValue:default(T);
試してください:
_t = result ? (T)(object)typedValue : default(T);
_
はい、ジェネリックは時々ちょっと迷惑なことがあります。
FWIW、私は はるかに単純なラッパー を使用してConvert.ChangeType()
を囲んでいますが、これは空の文字列の事前チェックを行うだけです。チェックされていないユーザー入力にこれを使用しているのでない限り、おそらくそれで十分です。
これを考えると:
したがって、Tは、int、uint、short、ushort、long、ulong、byte、sbyte、decimal、float、doubleのいずれかになります。
Convert.ChangeTypeを使用することをお勧めします。心配する必要はありません。例外が発生するのは、文字列の形式が間違っている場合のみです。この場合、default(T)を返すことができます。
つまり:
try
{
result = Convert.ChangeType(value, typeof(T));
}
catch
{
result = default(T);
}
ToTypeは、ここではジェネリックパラメーターです。これは、必要な場合に備えて、null許容型に対して機能します。メインメソッドをジェネリックコンバーターとして抽出し、null許容値を含む任意の型に変換できます。
ToType result = default(ToType);
result = ChangeType<ToType>(typedValue);
private T ChangeType<T>(object o)
{
Type conversionType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
return (T)Convert.ChangeType(o, conversionType);
}
シンプルなものを試すことができます
public static T ConvertValue<T,U>(U value) where U : IConvertible {
return (T)ConvertValue(value, typeof(T));
}
public static object ConvertValue(IConvertible value, Type targetType) {
return Convert.ChangeType(value, targetType);
}
リフレクションを使用して、組み込みのTryParseメソッドを使用しないのはなぜですか? Guidを除くすべてのネイティブ型にほぼ1つです。
public static Parser<T> GetParser<T>(T defaultResult)
where T : struct
{
// create parsing method
Parser<T> parser = (string value, out T result) =>
{
// look for TryParse(string value,out T result)
var parseMethod =
typeof(T).GetMethods()
.Where(p => p.Name == "TryParse")
.Where(p => p.GetParameters().Length == 2)
.Single();
// make parameters, leaving second element uninitialized means out/ref parameter
object[] parameters = new object[2];
parameters[0] = value;
// run parse method
bool success = (bool)parseMethod.Invoke(null, parameters);
// if successful, set result to output
if (!success)
{
result = (T)parameters[1];
}
else
{
result = defaultResult;
}
return success;
};
return parser;
}