C# Explicit operator and Object
Asked Answered
D

4

10

QUESTION

Please take a look to the code first.

Here is my custom class:

public class float2D
{
    public float X { get; private set; }
    public float Y { get; private set; }

    public float2D(float x, float y)
    {
        this.X = x;
        this.Y = y;
    }

    public static explicit operator Point(float2D x)
    {
        return new Point((int)x.X, (int)x.Y);
    }

    ...
}

And here is the test code I wrote:

    private void TEST()
    {
        float2D V = new float2D(1, 1);
        Point P = Point.Empty;
        P = (Point)V; // Works
        P = (Point)(V as object); // Specified cast is not valid.
    }

As you can see it failed to convert the value when value type is not known. I believe this happens because it search in Object class for operand not in real type. How can I solve this problem?

I have a code where EVERYTHING is object and it must take care of these conversations.

Please tell me if you have any Idea.


AVOIDING DYNAMIC

Ok let change the sample in the way you can see what I exactly want to do and what is my situation.

Here is the class I have:

    class TEST
    {
        dynamic OBJECT;
        public void STORE<T>(ref T V)
        {
            this.OBJECT = V;
        }

        public T CONVERT<T>()
        {
            return (T)this.OBJECT;
        }
    }

And here is the test code:

        float2D V = new float2D(1, 1);
        TEST t = new TEST();
        t.STORE(ref V);
        Point P = t.CONVERT<Point>();

Is there way I can drop dynamic from above class and keep it working? I really want to avoid .Net4/4.5

Delta answered 17/12, 2013 at 13:11 Comment(9)
See: Representation and identityRubberize
Aside here; why are you casting X and Y to integers?Snowber
@daveL - I'd assume its because the constructor for Point takes integers.Topheavy
The explicit and implicit conversion operators are evaluated at compile time. That means that the types as you have them at compile time must be fully known to apply those operators. When you convert V to object, you've essentially lost the type information and the compiler cannot make the determination to use the conversion operator you defined on float2D. However you can cheat using the dynamic keyword; this will cause the Dynamic Language Runtime to evaluate at runtime if there is a conversion operator or not. Try casting to object, then to dynamic, then to Point.Fenestra
That said, I don't really recommend using dynamic for that purpose unless you really need to; most likely you can solve it in other ways. Perhaps with a utility function that checks the type of the incoming object, casts it to its compile-time type (float2D) if it's compatible, then casts/converts it to a Point object.Fenestra
Son, I suggest you perform a big refactor on this project. Get rid of all those untype object variables.Crud
@Topheavy lol indeed it does. Time for lunch.Snowber
@ChrisSinclair, Thanks for information. I already solved it with Dynamic. Yet I really want to avoid it as it is a .Net4 thing. Please see edit.Delta
@SoroushFalahati: I've posted a couple options to work around this that should be compatible with .NET 2.0/3.5. Overall though, I think this points to some design issues with your application, though I suspect in this case it's a matter of legacy and not exactly viable to refactor it.Fenestra
F
3

As stated by other answers, you can't do this because you're trying to apply runtime casting to compile time conversion operations.

If you want to avoid dynamic because you don't want to use .NET 4.0's DLR, you could use reflection to find the conversion operators yourself. Can't comment on performance for your particular application doing this however:

public static TOutgoing Convert<TOutgoing>(object obj)
{
    Type incomingType = obj.GetType();

    MethodInfo conversionOperator = null;
    foreach(var method in incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public))
    {
        if (
            method.Name == "op_Explicit" && //explicit converter
            method.ReturnType == typeof(TOutgoing) && //returns your outgoing ("Point") type
            method.GetParameters().Length == 1 && //only has 1 input parameter
            method.GetParameters()[0].ParameterType == incomingType //parameter type matches your incoming ("float2D") type
            )
        {
            conversionOperator = method;
            break;
        }
    }

    if (conversionOperator != null)
        return (TOutgoing)conversionOperator.Invoke(null, new object[]{obj});

    throw new Exception("No conversion operator found");
}

Or for .NET 3.5 with LINQ:

public static TOutgoing Convert<TOutgoing>(object obj)
{
    Type incomingType = obj.GetType();

    var conversionOperator = incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Where(m => m.Name == "op_Explicit")
        .Where(m => m.ReturnType == typeof(TOutgoing))
        .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == incomingType)
        .FirstOrDefault();

    if (conversionOperator != null)
        return (TOutgoing)conversionOperator.Invoke(null, new object[]{obj});

    throw new Exception("No conversion operator found");
}

Usage:

float2D V = new float2D(1, 1);
Point P = Point.Empty;
P = Convert<Point>(V); //passes
P = Convert<Point>((V as object)); //passes

You can add "op_Implicit" as well if you wish to convert via implicit operators.


Another option if you want to avoid reflection is to pre-register the conversion functions, some casting and type lookups to determine which conversion operator to use.

Just a caveat, the solution here has a couple issues (thread safety, assumption that conversion functions exist, registering colliding/duplicate conversion functions throws errors) so use at your own risk or use it as a guide to modify as it suits your needs.

Basic gist is to define a simple converter to wrap the conversion functions themselves:

public interface IConverter
{
    object Convert(object incomingObject);
}

public class Converter<TIncoming, TOutgoing> : IConverter
{
    private Func<TIncoming, TOutgoing> ConversionFunction;

    public Converter(Func<TIncoming, TOutgoing> conversionFunction)
    {
        this.ConversionFunction = conversionFunction;
    }

    public object Convert(object incomingObject)
    {
        TIncoming typedIncomingObject = (TIncoming)incomingObject;
        return ConversionFunction(typedIncomingObject);
    }
}

Then a utility that you can register these conversions with:

public static class ConversionUtility
{
    private static Dictionary<Type, Dictionary<Type, IConverter>> Converters = new Dictionary<Type, Dictionary<Type, IConverter>>();

    public static void RegisterConversion<TIncoming, TOutgoing>(Func<TIncoming, TOutgoing> conversionFunction)
    {
        if (!Converters.ContainsKey(typeof(TIncoming)))
        {
            Converters[typeof(TIncoming)] = new Dictionary<Type, IConverter>();
        }

        Converters[typeof(TIncoming)].Add(typeof(TOutgoing), new Converter<TIncoming, TOutgoing>(conversionFunction));
    }

    public static TOutgoing Convert<TOutgoing>(object obj)
    {
        Type incomingType = obj.GetType();

        IConverter converter = Converters[incomingType][typeof(TOutgoing)];

        return (TOutgoing)converter.Convert(obj);
    }
}

For usage, first you must register the conversion functions you expect to use in your application (ideally perform the registration when your application starts up to avoid threading issues):

ConversionUtility.RegisterConversion((float2D obj) => (Point)obj);

Then your conversion usage:

float2D V = new float2D(1, 1);
Point P = Point.Empty;
P = ConversionUtility.Convert<Point>(V); //passes
P = ConversionUtility.Convert<Point>((V as object)); //passes

Not sure about the performance of one over the other for your particular application usage. The first sample is a bit more flexible as it performs the check at runtime and you don't have to pre-register the conversions you expect to use. The second might be a bit more stable as you only register the conversions you expect to use and there is no reflection, just casting and dictionary lookups.

Fenestra answered 17/12, 2013 at 15:16 Comment(1)
Thanks Chris. This is the only answer which address the real question.Delta
K
15

Yes, these two very different things. The first line:

P = (Point)V; // Works

uses the explicit conversion operator, which is overloaded for this combination. The second, however:

P = (Point)(V as object); // Specified cast is not valid.

this casts the reference V as an object (which we trivially know it is) - and then separately casts from object to Point.

Since Point is a struct, this then attempts to "unbox" that same reference as though it were a boxed Point. But it is not a boxed Point, so the error is correct. Conversion operators are not polymorphic, and unboxing is only allowed when the box contains a boxed copy of the appropriate type (caveat: you can unbox enums as integers, and integers as enums - as long as the underlying types match).

However, even if Point was a class, it would still fail with a similar error: reference-preserving casts also do not work if the types are incompatible.

Kemberlykemble answered 17/12, 2013 at 13:16 Comment(4)
Its worth pointing out that boxing occurs on Point because it is a structCrud
@MarcGravell Thanks for this helpful information. I have a question, can I somehow ask at run-time to use an other Type instead of object. Let say I know what the real type is but I kept value and type in two different variables. Object and Type. Can I convert this object to the type (I know it is possible with T but not sure if this is possible with Type) and then use explicit casting to converting it again to, for example Point?Delta
@SoroushFalahati there is no nice way to do the code in the question; there is, however, a simple but hacky way: P = (Point)(V as dynamic);. As for having the Type: that doesn't help you when it comes to conversion operators, unless you are going to write the reflection code yourself (to resolve and invoke the static conversion methods)Kemberlykemble
Would there be any major conceptual difficulty in having ValueType include a virtual unbox method whose default behavior would default to loading its contents from an Object with the correct type and throwing for anything else, but which could be overridden to allow other types as well, and having the unbox opcode invoke that method on the type in question?Caravaggio
F
3

As stated by other answers, you can't do this because you're trying to apply runtime casting to compile time conversion operations.

If you want to avoid dynamic because you don't want to use .NET 4.0's DLR, you could use reflection to find the conversion operators yourself. Can't comment on performance for your particular application doing this however:

public static TOutgoing Convert<TOutgoing>(object obj)
{
    Type incomingType = obj.GetType();

    MethodInfo conversionOperator = null;
    foreach(var method in incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public))
    {
        if (
            method.Name == "op_Explicit" && //explicit converter
            method.ReturnType == typeof(TOutgoing) && //returns your outgoing ("Point") type
            method.GetParameters().Length == 1 && //only has 1 input parameter
            method.GetParameters()[0].ParameterType == incomingType //parameter type matches your incoming ("float2D") type
            )
        {
            conversionOperator = method;
            break;
        }
    }

    if (conversionOperator != null)
        return (TOutgoing)conversionOperator.Invoke(null, new object[]{obj});

    throw new Exception("No conversion operator found");
}

Or for .NET 3.5 with LINQ:

public static TOutgoing Convert<TOutgoing>(object obj)
{
    Type incomingType = obj.GetType();

    var conversionOperator = incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Where(m => m.Name == "op_Explicit")
        .Where(m => m.ReturnType == typeof(TOutgoing))
        .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == incomingType)
        .FirstOrDefault();

    if (conversionOperator != null)
        return (TOutgoing)conversionOperator.Invoke(null, new object[]{obj});

    throw new Exception("No conversion operator found");
}

Usage:

float2D V = new float2D(1, 1);
Point P = Point.Empty;
P = Convert<Point>(V); //passes
P = Convert<Point>((V as object)); //passes

You can add "op_Implicit" as well if you wish to convert via implicit operators.


Another option if you want to avoid reflection is to pre-register the conversion functions, some casting and type lookups to determine which conversion operator to use.

Just a caveat, the solution here has a couple issues (thread safety, assumption that conversion functions exist, registering colliding/duplicate conversion functions throws errors) so use at your own risk or use it as a guide to modify as it suits your needs.

Basic gist is to define a simple converter to wrap the conversion functions themselves:

public interface IConverter
{
    object Convert(object incomingObject);
}

public class Converter<TIncoming, TOutgoing> : IConverter
{
    private Func<TIncoming, TOutgoing> ConversionFunction;

    public Converter(Func<TIncoming, TOutgoing> conversionFunction)
    {
        this.ConversionFunction = conversionFunction;
    }

    public object Convert(object incomingObject)
    {
        TIncoming typedIncomingObject = (TIncoming)incomingObject;
        return ConversionFunction(typedIncomingObject);
    }
}

Then a utility that you can register these conversions with:

public static class ConversionUtility
{
    private static Dictionary<Type, Dictionary<Type, IConverter>> Converters = new Dictionary<Type, Dictionary<Type, IConverter>>();

    public static void RegisterConversion<TIncoming, TOutgoing>(Func<TIncoming, TOutgoing> conversionFunction)
    {
        if (!Converters.ContainsKey(typeof(TIncoming)))
        {
            Converters[typeof(TIncoming)] = new Dictionary<Type, IConverter>();
        }

        Converters[typeof(TIncoming)].Add(typeof(TOutgoing), new Converter<TIncoming, TOutgoing>(conversionFunction));
    }

    public static TOutgoing Convert<TOutgoing>(object obj)
    {
        Type incomingType = obj.GetType();

        IConverter converter = Converters[incomingType][typeof(TOutgoing)];

        return (TOutgoing)converter.Convert(obj);
    }
}

For usage, first you must register the conversion functions you expect to use in your application (ideally perform the registration when your application starts up to avoid threading issues):

ConversionUtility.RegisterConversion((float2D obj) => (Point)obj);

Then your conversion usage:

float2D V = new float2D(1, 1);
Point P = Point.Empty;
P = ConversionUtility.Convert<Point>(V); //passes
P = ConversionUtility.Convert<Point>((V as object)); //passes

Not sure about the performance of one over the other for your particular application usage. The first sample is a bit more flexible as it performs the check at runtime and you don't have to pre-register the conversions you expect to use. The second might be a bit more stable as you only register the conversions you expect to use and there is no reflection, just casting and dictionary lookups.

Fenestra answered 17/12, 2013 at 15:16 Comment(1)
Thanks Chris. This is the only answer which address the real question.Delta
L
2

It is because you are casting it to object and you don't have an explicit cast for that - it won't implicitly assume you want it to be as a float2D.

Loon answered 17/12, 2013 at 13:14 Comment(4)
Yes Danial, Thanks for fast response. I already know that. I was asking if this is possible to solve this problem somehow? Because as I mentioned I have a code where everything is object and I need a way to handle these conversations.Delta
@SoroushFalahati can you use generics?Loon
@Danial, Yes and I tested with Generics too (for methods). Yet there is an other problem. I need to keep it in a variable. So when I convert Generic Type to Object (variable type) I actually lost real data type. I think the only way is to use dynamic (as variable data type)even though I didn't wanted to use it because I like to keep code in .Net2/3.5Delta
@SoroushFalahati I think you should add information about Framework version restriction in your question because in your case you can't use dynamic.Cheery
D
1

For a truly overengineered solution combining Chris Sinclair's answer using reflection with his memoized conversion methods in order to maintain performance while also not needing to call ConversionUtility.RegisterConversion()...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public static class ConvertUtil
{
    /// <summary>
    /// Converts <paramref name="source"/> to type <typeparamref name="TResult"/> 
    /// using reflection to determine the appropriate explicit or implicit cast
    /// operator.
    /// </summary>
    /// <typeparam name="TResult">
    /// The Type to convert <paramref name="source"/> to.
    /// </typeparam>
    /// <param name="source">The object being converted.</param>
    /// <returns>
    /// <paramref name="source"/> cast as <typeparamref name="TResult"/>.
    /// </returns>
    /// <remarks>
    /// Detects the type of <paramref name="source"/> type at run time.  This is a
    /// minor performance hit if used frequently.
    /// </remarks>
    /// <exception cref="InvalidCastException" />
    public static TResult ConvertTo<TResult>(object source) where TResult : class
    {
        if (source == null || sosurce is TResult)
        {
            return source;
        }

        Type sourceType = source?.GetType();
        return (TResult)GetConverter(sourceType, typeof(TResult))?.Invoke(source) ??
            throw new InvalidCastException($"No implicit or explicit cast from type" +
                $" {sourceType.Name} to {resultType.Name} exists.");
    }

    /// <summary>
    /// Converts <paramref name="source"/> to type <typeparamref name="TResult"/> 
    /// using reflection to determine the appropriate explicit or implicit cast
    /// operator.
    /// </summary>
    /// <typeparam name="TSource">
    /// The type <paramref name="source"/> is being converted from.
    /// </typeparam>
    /// <typeparam name="TResult">
    /// The Type to convert <paramref name="source"/>
    /// to.
    /// </typeparam>
    /// <param name="source">The object being converted.</param>
    /// <returns>
    /// <paramref name="source"/> cast as <typeparamref name="TResult"/>.
    /// </returns>
    /// <remarks>
    /// Detects the type of <paramref name="source"/> type at compile time for a
    /// slight performance gain over <see cref="ConvertTo{TResult}(object)"/> if used
    /// frequently.
    /// </remarks>
    /// <exception cref="InvalidCastException" />
    public static TResult ConvertTo<TSource, TResult>(TSource source) 
        where TSource : class 
        where TResult : class
    {
        if (source == null || sosurce is TResult)
        {
            return source;
        }

        Func<object, object> converter = 
            GetConverter(typeof(TSource), typeof(TResult)) ??
                throw new InvalidCastException($"No implicit or explicit cast from " +
                    $"type {sourceType.Name} to {resultType.Name} exists.");
        return (TResult)converter(source);
    }

    /// <summary>
    /// Lock for thread safety.  If this isn't an issue, you can remove this and all
    /// the <c>lock</c> portions of <see cref="GetConverter(Type, Type)"/>
    /// </summary>
    private static readonly object s_typeConvertersLock = new object();

    /// <summary>
    /// The map from source types to maps from destination types to their converting
    /// functions.
    /// </summary>
    private static readonly Dictionary<Type, Dictionary<Type, Func<object, object>>> s_typeConverters =
        new Dictionary<Type, Dictionary<Type, Func<object, object>>>();

    /// <summary>
    /// Retrieves the first implicit or explicit defined casting operator from
    /// <paramref name="sourceType"/> to <paramref name="resultType"/>.  Returns null
    /// if no such operator is defined.
    /// </summary>
    /// <param name="sourceType">The type being converted from.</param>
    /// <param name="resultType">The type being converted to.</param>
    /// <remarks>
    /// Only searches for the cast operator once per (<paramref name="sourceType"/>,
    /// <paramref name="resultType"/>) pair.
    /// </remarks>
    /// <returns>
    /// The first defined casting operator from <paramref name="sourceType"/> to
    /// <paramref name="resultType"/>. Null if no operator is defined.
    /// </returns>
    private static Func<object, object> GetConverter(Type sourceType, Type resultType)
    {
        // retrieve all the operators from sourceType that we know about if we know of
        // none, add an empty map to the dictionary
        if (!s_typeConverters.TryGetValue(sourceType, 
                out Dictionary<Type, Func<object, object>> sourceConverters))
        {
            lock (s_typeConvertersLock)
            {
                // check again in case another thread has already added the converter
                // dictionary while waiting for the lock
                if (!s_typeConverters.TryGetValue(sourceType, out sourceConverters))
                {
                    sourceConverters = new Dictionary<Type, Func<object, object>>();
                    s_typeConverters.Add(sourceType, sourceConverters);
                }
            }
        }

        // retrieve the operator from sourceType to resultType 
        // if we have not found it yet, search for it using reflection and add it to
        // the dictionary 
        // if no such cast operator exists, add still null to the dictionary so that
        // we don't need to search again
        if (!sourceConverters.TryGetValue(resultType, 
                out Func<object, object> converter))
        {
            lock (s_typeConvertersLock)
            {
                // check again in case another thread has already added the converter
                // while waiting for the lock
                if (!sourceConverters.TryGetValue(resultType, out castOperator))
                {
                    var castOperator =
                        (from method in resultType.GetMethods(BindingFlags.Static | BindingFlags.Public)
                         where (method.Name == "op_Explicit" | 
                                method.Name == "op_Implicit") &&
                             method.ReturnType == resultType
                         let paramInfo = method.GetParameters()
                         where paramInfo.Length == 1 && 
                            paramInfo[0].ParameterType == sourceType
                         select method).FirstOrDefault();

                    converter = 
                        source => castOperator?.Invoke(null, new object[] { source });
                    sourceConverters.Add(resultType, converter);
                }
            }
        }

        return converter;
    }
}

The reason this method is helpful (at least to me) is that Linq's IEnumerable<T>.Cast<TResult>() doesn't handle implicitly or explicitly defined casting operators, only base class or interface to subclass. I wanted to define my own overload of Cast (IEnumerable<T>.Cast<T, TResult>()) but found it troublesome because there was no way to tell the compiler that T can be cast to TResult. Given the above, this was my solution:

namespace System.Linq
{
    using System.Collections.Generic;

    public static class LinqExtensions
    {
        public static IEnumerable<TResult> Cast<T, TResult>(this IEnumerable<T> self)
        {
            return self.Select(item => ConvertUtil.Convert<T, TResult>(item));
        }
    }
}

Unfortunately, C# doesn't allow partial inference of generic functions so I actually have to specify T when calling myEnum.Cast<MyType, MyOtherType>().

As an aside, since I, personally, never call ConvertUtil.ConvertTo() in my code, I moved the memoization code into my extensions class, and put the code from ConvertUtil.ConvertTo<TSource, TResult>() directly in my IEnumerable<T>.Cast<T, TResult>() method. The primary reason for this is so that ConvertUtil.Convert() isn't called once per item--only once per IEnumerable<T>.Cast call. Of course, it's worth noting that usually myIEnum.Cast<MyType1, MyType2>() is longer than myIEnum.Select(i => (MyType2)i), so there's that.

Deane answered 12/7, 2018 at 0:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.