1

I have a class like the following for holding multiple objects.

At the Moment it is Possible to get Objects with .Get<T>(). I want to be able to get the objects by casting the MultiHolder<...>

MultiHolder<IHolderA, IHolderB, IHolderC> multiHolder = new MultiHolder<IHolderA, IHolderB, IHolderC>(new HolderA(), new HolderB(), new HolderC());

//Works
IHolderA HolderA_byGet = multiHolder.Get<IHolderA>();

//Works??!
IHolderB HolderB_byExplicitCast = (HolderB)multiHolder;

//Throws InvalidCastException
IHolderB HolderB_byEcplicitInterfaceCast = (IHolderB)multiHolder;

//Will not compile
//HolderC HolderC_byImplicitCast = multiHolder;

//Will not compile
//IHolderC HolderC_byImplicitInterfaceCast = multiHolder;

I first tried to cast it to IHolderB. Resulting in an InvalidCastException. Why wont it use the implicit cast?

While testing i discovered that casting it to 'HolderB' will work. Why does this work when Tholder2 is IHolderB.

Also, why am i not able to use implicit cassts, even though they are defined as such?


Following code, for compleate example:

using System;
using System.Collections.Generic;

namespace MultiHolderExample
{

    public interface IHolder{}

    public abstract class MultiHolder : IHolder
    {
        private readonly Dictionary<Type, IHolder> _holders;

        protected MultiHolder(Type[] types, IHolder[] holders)
        {
            _holders = new Dictionary<Type, IHolder>();

            for (int i = 0; i < types.Length; i++)
            {
                if (!types[i].IsInstanceOfType(holders[i]))
                    throw new ArgumentException($"Can't convert \"{holders[i].GetType().Name}\" to \"{types[i].Name}\"");
                _holders.Add(types[i], holders[i]);
            }
        }

        public T Get<T>() where T : IHolder
        {
            if (_holders.TryGetValue(typeof(T), out IHolder holder))
                return (T)holder;
            throw new ArgumentException("Unknown Type of holder " + typeof(T).Name);
        }
    }

    #region Generic holders
    public class MultiHolder<Tholder1, Tholder2> : MultiHolder
        where Tholder1 : IHolder
        where Tholder2 : IHolder
    {
        #region Constructor

        public MultiHolder(Tholder1 f1, Tholder2 f2) :
            base(new[] { typeof(Tholder1), typeof(Tholder2) }, new IHolder[] { f1, f2 })
        { }

        public static implicit operator Tholder1(MultiHolder<Tholder1, Tholder2> holder) => holder.Get<Tholder1>();
        public static implicit operator Tholder2(MultiHolder<Tholder1, Tholder2> holder) => holder.Get<Tholder2>();

        #endregion
    }

    public class MultiHolder<Tholder1, Tholder2, Tholder3> : MultiHolder
        where Tholder1 : IHolder
        where Tholder2 : IHolder
        where Tholder3 : IHolder
    {
        #region Constructor

        public MultiHolder(Tholder1 f1, Tholder2 f2, Tholder3 f3) :
            base(new[] { typeof(Tholder1), typeof(Tholder2), typeof(Tholder3) }, new IHolder[] { f1, f2, f3 })
        { }

        public MultiHolder(MultiHolder<Tholder1, Tholder2> mf1, Tholder3 f3) :
            base(new[] { typeof(Tholder1), typeof(Tholder2), typeof(Tholder3) }, new IHolder[] { mf1.Get<Tholder1>(), mf1.Get<Tholder2>(), f3 })
        { }

        public static implicit operator Tholder1(MultiHolder<Tholder1, Tholder2, Tholder3> holder) => holder.Get<Tholder1>();
        public static implicit operator Tholder2(MultiHolder<Tholder1, Tholder2, Tholder3> holder) => holder.Get<Tholder2>();
        public static implicit operator Tholder3(MultiHolder<Tholder1, Tholder2, Tholder3> holder) => holder.Get<Tholder3>();
        #endregion
    }
    #endregion

    public interface IHolderA : IHolder { }
    public class HolderA : IHolderA { public int HolderAVal; }

    public interface IHolderB : IHolder { }
    public class HolderB : IHolderB { public int HolderBVal; }

    public interface IHolderC : IHolder { }
    public class HolderC : IHolderC { public int HolderCVal; }
}
AntiHeadshot
  • 1,130
  • 9
  • 24
  • 3
    Implicit cast to interface doesn't work because user defined implicit conversion to\from interface are not allowed. That means you cannot do `public static implicit operator IHolderA(MultiHolder holder) => holder.Get();` - that will not compile. When you have generic type - it will compile, because generic parameter can be interface, but it can be not. But compiler will still enforce that rule, so when generic parameter is interface (like in your example) - implicit operator is ignored. Why cast to HolderB works - I have no idea. – Evk Feb 21 '18 at 09:34

0 Answers0