tags:

views:

2460

answers:

6

HI!

Here is my case: I have some value type which is wrapped into another type with appropriate implicit converters. If I cast wrapped type to an object and then try to get original value I can do that in two-step cast only. If simplified my code is as follows:

public enum MyEnum : int
 {
  First,
  Second
 }


 public class Test<T>
 {
  public Test(T val)
  {
   Value = val;
  }

  private T Value { get; set; }


  public static implicit operator T(Test<T> m)
  {
   return m.Value;
  }

  public static implicit operator Test<T>(T m)
  {
   var res = new Test<T>(m);
   return res;
  }
 }


 static void Main()
 {
  object res = new Test<MyEnum>(MyEnum.First);
  Console.WriteLine((MyEnum)(Test<MyEnum>)res);
  Console.WriteLine((MyEnum)res);
 }

First "Console.WriteLine" works OK. Second one fails.

Is there any way I can modify this behavior and get it working without double casting?

UPDATE 1

I must use object to value cast (in real application I have to cast ComboBox.SelectedItem property and I do not want to add extra property to ComboBox, because I'll have to change my UI interaction code everywhere).

UPDATE 2

Implicit conversions to and from System.Object are not allowed.

UPDATE 3

Updated my sample code to reflect the whole problem.

+5  A: 

Don't use object that way. Write your first line like this instead:

Test res = new Test(1);

If you must have it in an object first, remember that all the compiler knows about it at this point is that it's an object, and nothing more. You, as the programmer, have additional information about what you expect this object to be, but for the compiler to take advantage of that information you have to put it into your code somewhere.

Update:
I'm glad I was able to find this again, because this almost-very-timely article by Eric Lippert, who works on the C# language design, went up this morning and explains the problem in depth:
http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx

Joel Coehoorn
I've added an update which explains why I can not do so.
Nikolay R
+1  A: 

or even

var res = new Test(1);
Jhonny D. Cano -Leftware-
I've added an update which explains why I can not do so.
Nikolay R
+1  A: 

Your local variable res is always of type object; so the line that isn't working is trying to convert an object, that isn't an int, to an int, which can't be done. Same as this fails:

        object d = 5.5d;
        Console.WriteLine((int)d);

EDIT:

Perhaps a pattern that might help is something like this:

        if (res.GetType() == typeof(Test))
        {
            Console.WriteLine((int)(Test)res);
        }
        else
        {
            Console.WriteLine((int)res);
        }

It's a very localized solution to your problem, but perhaps it will work for you.

Timothy Carter
Exactly. Is there any way I can control that?
Nikolay R
What do you mean by control?
Timothy Carter
Do some magic in order for this to work :) Console.WriteLine((int)(double)d); - without the *double* part?
Nikolay R
Pattern, you've suggested may work but I have a hard feeling toward using it. Maybe I'll end up with such code..
Nikolay R
A: 

While the error is due to res being of type object, I would make the Test->int operator explicit...

Lee
does not work either :(
Nikolay R
+2  A: 

Instead of adding implicit operators, consider implementing IConvertible. You only need to implement the ToInt32 method, the others are meaningless and you can throw the InvalidCastException in the other methods.

After that, you can use Convert.ToInt32() method to convert your object in one step.

Dave Van den Eynde
Sorry for not specifying my task entirely. Actually I'm working with Enums and my wrapper is generic<EnumType>. So I can not use IConvertible
Nikolay R
Updated code appropriately
Nikolay R
+1  A: 

If you want to simplify casting and not care performance effect, then create extension method.

public static T To<T>(this object obj) {
    Type type = obj.GetType();
    MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
    MethodInfo method = methods.FirstOrDefault(mi => (mi.Name == "op_Implicit" || mi.Name == "op_Explicit") && mi.ReturnType == typeof(T));
    if (method == null)
        throw new ArgumentException();
    return (T)method.Invoke(null, new[] { obj });
}

Usage

Console.WriteLine(res.To<MyEnum>());
chaowman
That's what I came up with myself - Extender. Thank you! Will mark this as answer.
Nikolay R