Ho finito per gestire manualmente lo scenario dei tipi primitivi. Non molto elegante, ma funziona.
Ho anche aggiunto ulteriore logica per gestire i tipi nullable e le enumerazioni.
Ho riutilizzato Poke's code per lo scenario di tipo definito dall'utente.
public class AvailableCastChecker
{
public static bool CanCast(Type from, Type to)
{
if (from.IsAssignableFrom(to))
{
return true;
}
if (HasImplicitConversion(from, from, to)|| HasImplicitConversion(to, from, to))
{
return true;
}
List<Type> list;
if (ImplicitNumericConversions.TryGetValue(from, out list))
{
if (list.Contains(to))
return true;
}
if (to.IsEnum)
{
return CanCast(from, Enum.GetUnderlyingType(to));
}
if (Nullable.GetUnderlyingType(to) != null)
{
return CanCast(from, Nullable.GetUnderlyingType(to));
}
return false;
}
// https://msdn.microsoft.com/en-us/library/y5b434w4.aspx
static Dictionary<Type,List<Type>> ImplicitNumericConversions = new Dictionary<Type, List<Type>>();
static AvailableCastChecker()
{
ImplicitNumericConversions.Add(typeof(sbyte), new List<Type> {typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(byte), new List<Type> { typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(short), new List<Type> { typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(ushort), new List<Type> { typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(int), new List<Type> { typeof(long), typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(uint), new List<Type> { typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(long), new List<Type> { typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(char), new List<Type> { typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
ImplicitNumericConversions.Add(typeof(float), new List<Type> { typeof(double) });
ImplicitNumericConversions.Add(typeof(ulong), new List<Type> { typeof(float), typeof(double), typeof(decimal) });
}
static bool HasImplicitConversion(Type definedOn, Type baseType, Type targetType)
{
return definedOn.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
.Any(mi =>
{
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
return pi != null && pi.ParameterType == baseType;
});
}
}
Solo un suggerimento: Guarda [operatori di conversione di tipo implicita] (https://msdn.microsoft.com/en-us/library/z5z9kes2.aspx). Immagino che ci dovrebbe essere un modo per trovare gli operatori impliciti attraverso la riflessione ... – elgonzo
Puoi per favore entrare in maggiori dettagli su quale obiettivo stai cercando di ottenere con il risultato? Ho avuto un problema simile in passato, ho capito che avrebbe dovuto somigliare a quello che CliveDM [linkato] (https://github.com/CYJB/Cyjb/blob/f4424c4f81cacd09e9ce5d202a03b0c121c09ac2/Cyjb/TypeExt.cs) [sotto ] (http://stackoverflow.com/a/32025388/1083771), e ha deciso di chiamare semplicemente [Convert.ChangeType] (https://msdn.microsoft.com/en-us/library/dtb69x08.aspx) e gestire eccezioni. Mi rendo conto che potrebbe non essere una soluzione valida nel tuo caso, ma forse c'è una soluzione alternativa simile. –