The following code works fine:
public static void Main()
{
Foo<int>(5);
}
private static void Foo<T>(T x)
{
Bar((int)(object)x);
}
private static void Bar(int x)
{
}
However, my Bar method comes from a third-part library (Json.NET) that implements multiple overloads. Example:
private static void Bar(string x)
{
}
If I call Foo<int>(5), the Bar((int)(object)x) conversion works fine, but calling Foo<string>("") breaks at runtime (for obvious reasons, you can't convert string to int).
So, I would like to change the Bar((int)(object)x) conversion to a generic Bar((T)(object)x) conversion, but this gives the following compilation error:
cannot convert 'T' to 'int'
So, is it possible to convert object to T or the only solution is to using a switch-case convertion?
The error actually means that the compiler doesn't know which overload of Bar to invoke, since T could be any type at compile time.
You would need check if T is int or string at runtime and explicitly cast to the needed type.
if (typeof(T) == typeof(int))
Bar((int)(object)x);
else if (typeof(T) == typeof(string))
Bar((string)(object)x);
else
throw new Exception();
Related
I'm experimenting with custom integer types and came across an interesting issue involving generics, implicit conversions, and 32 bit integers.
Below is a stripped down example of how to reproduce the problem. If I have two implicit methods that convert int to MyInt and vice versa, I get a compilation error which looks like C# can't resolve which generic type to use. And it only happens with int or uint. All other integer types work fine: sbyte,byte,short,ushort,long,ulong.
If I remove one of the implicit conversion methods, it also works fine. Something to do with circular implicit conversions?
using Xunit;
public class MyInt
{
public int Value;
//If I remove either one of the implicit methods below, it all works fine.
public static implicit operator int(MyInt myInt)
{
return myInt.Value;
}
public static implicit operator MyInt(int i)
{
return new MyInt() { Value = i };
}
public override bool Equals(object obj)
{
if (obj is MyInt myInt)
{
return this.Value == myInt.Value;
}
else
{
int other_int = (int)obj;
return Value == other_int;
}
}
}
Below is the test code showing the compilation errors I get when both implicit methods are defined.
public class Test
{
[Fact]
public void EqualityTest()
{
MyInt myInt = new MyInt();
myInt.Value = 4 ;
Assert.Equal(4, myInt.Value); //Always OK which makes sense
//Compile errors when both implicit methods defined:
// Error CS1503 Argument 1: cannot convert from 'int' to 'string',
// Error CS1503 Argument 2: cannot convert from 'ImplicitConversion.MyInt' to 'string'
Assert.Equal(4, myInt);
}
}
I believe C# is complaining about not being able to convert both types to string as that is the type of the last Xunit.Assert.Equal() overload and all the others failed to match:
//Xunit.Assert.Equal methods:
public static void Equal<T>(T expected, T actual);
public static void Equal(double expected, double actual, int precision);
public static void Equal<T>(T expected, T actual, IEqualityComparer<T> comparer);
public static void Equal(decimal expected, decimal actual, int precision);
public static void Equal(DateTime expected, DateTime actual, TimeSpan precision);
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual, IEqualityComparer<T> comparer);
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual);
public static void Equal(string expected, string actual, bool ignoreCase = false, bool ignoreLineEndingDifferences = false, bool ignoreWhiteSpaceDifferences = false);
public static void Equal(string expected, string actual);
I don't think I've made a mistake with the implicit conversions as I can make other similar examples create the same problem when used with 32 bit ints.
I'm testing in a .NET Core 3.0 project.
Any help would be appreciated. Thanks!
Clarification:
What I would like to know is why this only fails with 32 bit integers. Implicit conversions are working (confirmed with debugging) when the types are anything else like the example below using a long.
using Xunit;
public class MyLong
{
public long Value;
public static implicit operator long(MyLong myInt)
{
return myInt.Value;
}
public static implicit operator MyLong(long i)
{
return new MyLong() { Value = i };
}
public override bool Equals(object obj)
{
if (obj is MyLong myInt)
{
return this.Value == myInt.Value;
}
else
{
long other_int = (long)obj;
return Value == other_int;
}
}
}
public class Test2
{
[Fact]
public void EqualityTest()
{
MyLong myLong = new MyLong();
myLong.Value = 4 ;
Assert.Equal(4, myLong); //NOTE! `4` is implicitly converted to a MyLong
//object for comparison. Confirmed with debugging.
}
}
Something to do with circular implicit conversions?
Yes (though, you've already demonstrated that much by showing that it works fine when one of the conversions is eliminated).
The reason this is happening with int, and not with other types, is that the type of your literal is int. This means that during overload resolution, the compiler can go either way: convert int to MyInt, or convert MyInt to int. Neither option is clearly "better" than the other, so neither of those conversions survive consideration.
Then, having ruled out the closest possible generic version of the method, of the remaining overloads available the only one left is the Equal(string, string) overload (the only other one left with just two parameters is the Equal<T>(IEnumerable<T>, IEnumerable<T>), which is "worse" than the Equal(string, string) overload according to the overload resolution rules). Having found exactly one method that is clearly "better" than any others, the compiler then tries to use that method with your parameters, which of course don't fit, causing the errors to be emitted.
On the other hand…
When you try to call Equal(4, myLong), you've got two incompatible types. A literal having type int, and the MyLong value. In this case, the compiler tries each parameter one by one and finds that when it uses the MyLong type as the type parameter, it is possible to promote the int literal to a long and then implicitly convert that to MyLong. But it can't go the other way. It's not possible to select int as the generic type parameter, because MyLong can't be implicitly converted to int. So in that case, there is a "better" overload to choose, and so it's chosen.
By explicitly specifying the literal's type, you can try different combinations and see this pattern at work. First, I prefer a simpler wrapper class to test with:
public class Wrapper<T>
{
public T Value;
public static implicit operator T(Wrapper<T> wrapper) => wrapper.Value;
public static implicit operator Wrapper<T>(T value) => new Wrapper<T> { Value = value };
}
Then try these:
Wrapper<int> w1 = new Wrapper<int> { Value = 4 };
Wrapper<long> w2 = new Wrapper<long> { Value = 4 };
Assert.Equal(4, w1); // error
Assert.Equal((short)4, w1); // no error
Assert.Equal(4, w2); // no error
Assert.Equal(4L, w2); // error
The only thing that makes int special is that that's the default type for the numeric literal. Otherwise, a type that wraps int works exactly the same as a type that wraps anything else. As long as a conversion is available only in one direction between the two parameters, everything's fine. But when the conversion is available in both directions, the compiler has no choice but to throw up its hands and give up.
Can someone help me to understand why the below doesn't work?
void test(int i)
{
Console.WriteLine("int");
}
void test(String s)
{
Console.WriteLine("String");
}
void runMe()
{
object obj = 1;
Type t = typeof(int);
test((t)obj);
}
You get a "The type or namespace name 't' could not be found" error.
Is there a way to make this work? I need to cast an object to a specific type known only at runtime, but all options I've found are simply converting the data but still storing them in an object.
Edit: Added some pseudo methods to give more context.
It looks like you're basically trying to perform dynamic dispatch. The way you're trying it won't work, because while you work with static typing, all overload resolution is performed at execution.
However, you can use dynamic typing for this instead, with the dynamic type. At that point, overload resolution is performed at execution time instead:
void Test(int i)
{
Console.WriteLine("int");
}
void Test(String s)
{
Console.WriteLine("String");
}
void RunMe()
{
dynamic obj = 1;
// The right overload is picked at execution time
Test(obj);
}
That will accomplish what you've shown in your question - but it's not necessarily the best approach. If you can possibly stick to static typing (without using reflection) I would do so. If you can only handle a known set of types, you might want to keep a Dictionary<Type, Action<object>> or something like that... although you then need to consider awkward things like subtyping etc.
If you do use dynamic typing, I'd try to use it for just a small piece of your code. As soon as you can get back "out" of dynamic typing, do so (e.g. by casting the result of a dynamically-bound call to its expected return type).
Here's an example with a few ways to do it combined into one example.
First method is to overload your method for supported types (these can get called directly when type is known at compile-time) and create a catch-all default overloaded method with parameter of type object which internally checks for supported types and calls the appropriate type-specific method (this is for types only known at run-time).
Second method can be used as an extension to the first, but you can also implement just the second method and skip the first part. Since you know the type at the start of the run-time and the type is not expected to change with each call, you can skip the per-call type checking and instead do a check once when you load the config, then set the appropriate delegate.
try it: https://dotnetfiddle.net/06JYE1#
using System;
public class Program
{
public static void Main()
{
var p = new Program();
object s = "Hi";
object i = 42;
object f = 3.14;
p.Test(s);
p.Test(i);
p.Test(f);
p.SetTestType(GetConfigType());
p.ConfiguredTest("Hello");
p.ConfiguredTest(s);
}
public static Type GetConfigType() { return typeof(string); }
Action<object> ConfiguredTest;
void SetTestType(Type type)
{
if (type == typeof(string))
ConfiguredTest = o => Test((string)o);
else if (type == typeof(int))
ConfiguredTest = o => Test((int)o);
else
ConfiguredTest = null;
}
void Test(object o) // catch-all when type is not known until runtime
{
if (ConfiguredTest != null)
{
ConfiguredTest(o); // if type is configured, we can skip type checking
}
else // if type is not configured, check for supported types
{
if (o is string)
Test((string)o);
else if (o is int)
Test((int)o);
else
Console.WriteLine("Unsupported type: " + o.GetType());
}
}
void Test(int i) { Console.WriteLine("Int = " + i); }
void Test(String s) { Console.WriteLine("String = " + s); }
}
I'm new to C# and I'm trying to make a generic type dispatcher (C++ like), but it looks like I can't call a function with a generic parameter:
Sample code:
class Program
{
static void Main(string[] args)
{
DoSomething(1);
}
static void DoSomething<T>(T value)
{
//error CS1503: Argument 1: cannot convert from 'T' to 'int'
DoSomethingElse(value);
}
static void DoSomethingElse(int a)
{
Console.Write("int\n");
}
static void DoSomethingElse(float a)
{
Console.Write("float\n");
}
}
Can you please explain why I can't call DoSomethingElse from DoSomething?
How may I forward value to another function that accepts that specific type?
This is probably not the cleanest way to deal with it, but you can cast to dynamic.
The problem is, if you don't have a method implemented for whatever type T is, it will crash at runtime. You may be able to narrow that problem a bit by adding a constraint to your generic parameter.
static void DoSomething<T>(T value) where T : struct
{
DoSomethingElse((dynamic)value);
}
Without any constraints, you are only permitted to do activities that you could do if value is of type object. You can use the is and as keywords to work around this.
If you add a where constraint such as where T : IComparable, you are permitted to use methods and properties that meet the constraint.
i.e. with the IComparable constraint, you could call value.CompareTo(x)
static void DoSomething<T>(T value)
{
if (value is int)
{
DoSomethingElse((value as int?).Value);
}
else if (value is float)
{
DoSomethingElse((value as float?).Value);
}
else
{
throw new Exception("No handler for type " + typeof(T).Name);
}
}
I have a method, it doesn't have any T parameters.
Unfortunately, C# typeof operator doesnt work on generics as shown bellow.
public static string ToString(object obj)
{
if (obj.GetType() == typeof(List<object>))
{
return "Bug: Never called";
}
if (obj.GetType() == typeof(List<int>))
{
return "Method only works for int lists";
}
return "";
}
Expected output: ToString(List of ints) would return "Bug: Never Called".
Actual output: "Method only works for int lists"
This worked perfectly fine in Java. You will say that generics in c# don't support this. How can I restructure my code to make "ToString(object)" method work for a list of objects?
Requirements : You cannot modify the method's signature, only its implementation.
Unfortunately you cannot make "int" class implement an interface. Since int class was written by Microsoft. As such I cannot add a visitor pattern on int type.
Unfortunately, c# typeof operator doesn't work on generics as shown bellow.
Actually, it's quite fortunate that it doesn't work as you expect it to work. Unlike Java, C# differentiates between List<int> and List<object> at runtime, not only at compile time. Type information for generics instantiated with primitive type parameters lets .NET produce more efficient code by avoiding type erasure.
If you would like to test if an object is any generic list, use this check instead:
var objT = obj.GetType();
if (objT.IsGenericType && objT.GetGenericTypeDefinition() == typeof(List<>)) {
...
}
As such I cannot add a visitor pattern on int type.
C# gives you a powerful mechanism for dispatching dynamically without implementing an interface. For example, you can do this:
public static string ToString(object obj) {
var list = obj as IList;
if (list != null) {
foreach (dynamic o in list) {
Process(o);
}
}
}
static void Process(int n) { ... }
static void Process(string s) { ... }
static void Process(object o) { ... }
The above will choose the right implementation of Process based on the run-time type of the object in your list.
public interface ILovable<T> where T : IEquatable<T>
{
T Care(T t);
}
public class Me : ILovable<int>
{
public int Care(int i)
{
return i;
}
}
Say I have the above. Now below function fails:
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
{
var z = me.Care(1); //cannot convert from 'int' to 'T'
}
What's failing the above piece of code? ILovable<T> has a Care function which intakes a T which is IEquatable<T>. In the above function I'm calling the same Care function and passing T which is int type. int is after all IEquatable<int>.
What am I doing wrong? Is there any work around to get it fixed?
Your method signature does not specify a ILovable<int>, it specifies an ILovable<T>. This, for example, would work:
private static void Colour(ILovable<int> me)
{
var z = me.Care(1); //cannot convert from 'int' to 'T'
}
The problem is the compiler doesn't know that T is an 'int' in your example; it could be any type that meets the constraint. Here is another way that would work:
private static void Colour<T>(ILovable<T> me, T valueToCareAbout) where T : IEquatable<T>
{
var z = me.Care(valueToCareAbout);
}
//use like this
Colour(me, 1);
The error I get is:
Argument type 'int' is not assignable to parameter type 'T'
I'm pretty sure this is because you are defining me as an ILovable<T>. Therefore, it doesn't automatically resolve to the Me type where int is defined as T.
This will fix the error because Me defines T as an int:
private static void Colour<T>(Me me) where T : IEquatable<T>
{
var z = me.Care(1);
}
Well that's because method Colour says that there will be parameter of type ILovable< T > whereas T would be resolved later, so at compile time either I tell method that T is int type.
So either you pass ILovable as parameter and grantee that T is int
void Colour<T>(ILovable<int> me)
or pass type Me directly
void Colour<T>(Me me)
Because otherwise me.Care is expecting type T not int as specific
Change following
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
To
private static void Colour<Int32>(ILovable<int> me)
and above will work.
Now the mystry portion
You are getting error in following
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
because Care is expecting T, and you are providing int.
It is same as
Care((T)1).
or
T t = (T)1; //This is the cause of error as int cannot be changed to T. Remember Int32 is sealed so T cannot derive from int
Care(t); // This is fine
To make above work, T has to int. To make it so, Colur method syntax should be like
private static void Colour<Int32>(ILovable<int> me)
If you want to pass string to Care, T should be string.
private static void Colour<string>(ILovable<string> me)
{
me.Care("Hello");
}
Now if we have to fix T then question arises why T is required at all in Colour definition.
Answer -> For non taking care of inheritance type in non sealed class.
The short answer is overriding variable of type T inside a generic method (or class) with a more derived type is not possible since compiler doesn't explicitly know T is that more derived type (in our case T is int), because T can be any other more derived type at run time.
Long answer: me variable is of type ILovable<T>. Now me.Care function is expecting parameter of type that is specified on ILovable<T> which is T. Outside the Care function T can be anything that is IEquatable<T>, so int is ok. But inside the function, T has to be just T and not another derived type of IEquatable<T>. Otherwise there will be runtime error for scenarios like this:
private static void Colour<T>(ILovable<T> me) where T : IEquatable<T>
{
var z = me.Care(1);
}
...
Colour("");
Right now, T is string when calling Colour(""). So me is ILovable<string>. So me.Care function expects a string as parameter but provided is an int and that is disaster.