Похоже, вы предполагаете, что тип из arg1 - это тот, в который вы хотите конвертировать, поэтому я бы использовал подобный род. Пока arg2 является IConvertible (int, double, все числа, строки и т. Д. Все IConvertible), это будет работать:
public static bool ValueEquality<T1, T2>(T1 val1, T2 val2)
where T1 : IConvertible
where T2 : IConvertible
{
// convert val2 to type of val1.
T1 boxed2 = (T1) Convert.ChangeType(val2, typeof (T1));
// compare now that same type.
return val1.Equals(boxed2);
}
** ОБНОВЛЕНИЕ ** Сделаны оба типа обобщенных аргументов, могут быть выведены и добавляет больше безопасности времени компиляции на arg2, чтобы убедиться, что он IConvertible во время компиляции.
Учитывая эту универсальную функцию, все последующие теперь возвращают true (не нужно указывать аргумент типа, поскольку выводится из первого аргумента:
Console.WriteLine(ValueEquality(1, "1"));
Console.WriteLine(ValueEquality(2, 2.0));
Console.WriteLine(ValueEquality(3, 3L));
UPDATE
Основываясь на вашем комментарии, вот перегрузка, если все, что у вас есть, это объекты. Оба могут сосуществовать, и он вызовет более подходящий на основе аргументов:
public static bool ValueEquality(object val1, object val2)
{
if (!(val1 is IConvertible)) throw new ArgumentException("val1 must be IConvertible type");
if (!(val2 is IConvertible)) throw new ArgumentException("val2 must be IConvertible type");
// convert val2 to type of val1.
var converted2 = Convert.ChangeType(val2, val1.GetType());
// compare now that same type.
return val1.Equals(converted2);
}
И это будет работать для объекта:
object obj1 = 1;
object obj2 = 1.0;
Console.WriteLine(ValueEquality(obj1, obj2));
Как я уже сказал, оба из них могут сосуществовать в виде перегрузок, поэтому, если вы сравниваете совместимые типы IConvertible напрямую, он будет использовать универсальный тип, а если вы просто используете коробочные типы в качестве объекта, он будет использовать перегрузку объекта.