-1

The problem I am getting is that, in the below example, I can cast MyType<T> as IMyType, fine. However I cannot cast Func<MyType<T>, bool> as Func<IMyType, bool>.

That doesn't make any sense and I'm wondering if there is a way round it through perhaps different architecture?

I do not want to use reflection.

I would also like to understand why it is failing.

Code failing in .NET 4.5.2. Also online version here - http://goo.gl/13z4xg - fails in the same way.

using System;
using System.Collections.Generic;

public interface IMyType
{
    string Text { get; }
    int Number { get; }
}

public class MyType<T> : IMyType where T : SomeOtherType
{
    public MyType()
    {
        Text = "Hello";
        Number = 99;
    } 
    public string Text { get; private set; }
    public int Number { get; private set; }
}

public abstract class SomeOtherType
{
    public int Id { get; set; }
    public string Title { get; set; }
}

public class ConcreteType : SomeOtherType
{
    public string Description { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var ok = (IMyType) new MyType<ConcreteType>();
        Console.Write("I can cast MyType<T> to IMyType here");
        var list = new List<Func<IMyType, bool>>();
        Func<MyType<ConcreteType>, bool> func = type => false;
        // list.Add(func); // Compiler error
        list.Add((Func<IMyType,bool>)func); // Runtime cast error
        Console.Write("Got here so ok"); // (It doesn't get here...)
    }
}
LiverpoolsNumber9
  • 2,384
  • 3
  • 22
  • 34
  • 2
    What version of the runtime are you using? This is not giving me an error (other than the compile error caused by the missing `func` name). Note this is assuming empty definitions for `IMyType`, `MyType`, `SomeOtherType`, and `ConcreteType : SomeOtherType` – Dax Fohl Apr 23 '15 at 00:08
  • The code example you posted is "fine" (in that it doesn't seem to have any way to throw a run-time exception as you describe). Did you mean to write `new List>()` instead? That would fail to work in a well-understood way (though, at compile-time, not run-time). Clearly, you have not posted the actual code that's failing. Please edit your post so that it contains [a good, _minimal_, _complete_ code example](http://stackoverflow.com/help/mcve) that reliably reproduces the problem. – Peter Duniho Apr 23 '15 at 04:31
  • @DaxFohl Yes, syntax error – LiverpoolsNumber9 Apr 23 '15 at 07:18
  • @PeterDuniho Clearly, I have not posted the actual code because clearly I would lose my job if I did. I will edit with an example now. It does fail at runtime. – LiverpoolsNumber9 Apr 23 '15 at 07:19
  • The point is, whatever code you post, _must reproduce the problem you're asking about_. The first code example didn't. With your new example, I see that as I guessed, you are making a well-known mistake. There are a number of posts on Stack Overflow that discuss this; I would propose to close this as a duplicate, but I already proposed to close it due to the lack of a good code example. Feel free to vote to close as a duplicate of your favorite from the following list of posts: http://stackoverflow.com/q/2033912/3538012, http://stackoverflow.com/q/8567206/3538012, (continued...) – Peter Duniho Apr 23 '15 at 07:39
  • (more duplicates...) http://stackoverflow.com/q/2717288/3538012, http://stackoverflow.com/q/833447/3538012, http://stackoverflow.com/q/6557/3538012, http://stackoverflow.com/q/4038125/3538012 – Peter Duniho Apr 23 '15 at 07:40
  • @PeterDuniho Yes, that answers the "why" and is much appreciated. But the "what to do" is still a mystery. Any chance you could spend time helping with that rather than trying to get the question closed? After all, this is a question > answer site. I still don't have the latter. – LiverpoolsNumber9 Apr 23 '15 at 07:58

3 Answers3

1

Sorry...I thought the various answers explained pretty well why you don't want to do this. Even though you think you do (a common enough mistake, so no reason to feel badly about it). It didn't occur to me that after reading all of those questions and their answers, you wouldn't feel you had an adequate answer to your own question. I'm not sure I can add much to them, but I'll try…


The short version: if you were allowed to add an instance of Func<MyType<ConcreteType>, bool> to a list of Func<IMyType, bool>, then some other code could pull that instance out as a Func<IMyType, bool> and then try to call it, passing to the delegate an instance of IMyType other than MyType<ConcreteType>.

But, your method (presumably, given the delegate declaration) expects only instances of MyType<ConcreteType>! What would it do if it were passed some other implementation of IMyType?


The compiler tried to help prevent you from making that mistake, when it gave you the compile-time error at list.Add(func). You hid the error from the compiler with the cast, but that doesn't change the fundamental hazard the compiler is trying to save you from, and so the run-time has to step in when you attempt the cast and stop you there.

It would have been better to block the code at compile-time, but that's not possible when you use a cast to claim you know something about the type that the compiler doesn't. But it's still better to get the run-time error there at the cast, rather than later when you try to pass some other implementation of IMyType to the method the delegate represents.


Now, the code example you've given doesn't make very clear why you think this would even be advantageous to do. So it's impossible to know what alternative would be best in your case.

If the method the delegate reference does indeed need to know the type is MyType<ConcreteType>, then you're stuck. It simply is not safe to put a delegate referencing that method in a context where any IMyType could get passed to it. You painted yourself into a corner somehow, and you need to rethink your design, because it currently has a fundamental flaw in it.

Note, however, that if you really only use the IMyType-ness of the object in your method, then the solution is to just declare it that way. For example, the lambda expression you show would compile just fine if assigned instead to a variable of type Func<IMyType, bool>, which you could add to the list.

Alternatively, if you are 100% certain that no code will ever attempt to invoke the delegate with an implementation of IMyType other than MyType<ConcreteType>, you could wrap the wrong delegate type in an instance of the right delegate type. For example:

list.Add(o => func((MyType<ConcreteType>)o));

Of course, by doing that you will completely remove the bulk of the type-safety features of using the generic List<T> object. It definitely has that "bad code smell" to it. But it would compile, and would even run as long as you never break the rules and try to invoke the delegate with a different implementation of IMyType.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • Thanks. I understand the type-safety aspect, I was really looking for a way to program for the specific situation I described, as well as an explanation of "why" (which you've provided here). FWIW I disagree with "you don't want to do this". In this circumstance, I need to do this, and I am handling the potential type-safety issues (in my real world code). – LiverpoolsNumber9 Apr 23 '15 at 08:50
0

Answering my own question, or at least the "so how can I make it work" part.

The issue is variance (see duplicates in comments). The answer in this instance to the "how can I make it work" part is to use List<T>.ConvertAll (as suggested here - C# variance problem: Assigning List<Derived> as List<Base> by Jon Skeet).

For what I need, this solution is perfect. The "expense" of creating a new list doesn't matter in my circumstances.

So what I've done is expanded on the example, and used List<T>.ConvertAll so that the non-generic-aware instance can access the functions. Link to code here - http://goo.gl/MVnYhX - code also below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    public interface IMyType
    {
        string Text { get; }
        int Number { get; }
    }
    public class MyType<T> : IMyType where T : SomeOtherType
    {
        public MyType()
        {
            Text = "Hello World";
            Number = 999;
        } 
        public string Text { get; private set; }
        public int Number { get; private set; }
    }
    public abstract class SomeOtherType
    {
        public int Id { get; set; }
        public string Title { get; set; }
    }
    public class ConcreteType : SomeOtherType
    {
        public string Description { get; set; }
    }
    public interface IFaceThatAccessesIt
    {
        IList<Func<IMyType, bool>> GetList();
    }
    public class ClassThatAccessesIt<T> : IFaceThatAccessesIt where T : SomeOtherType
    {
        /// <summary>
        /// Generically typed inner list
        /// </summary>
        private readonly List<Func<MyType<T>, bool>> _innerList = new List<Func<MyType<T>, bool>>();

        /// <summary>
        /// Get list converted
        /// </summary>
        /// <returns></returns>
        public IList<Func<IMyType, bool>> GetList()
        {
            var output = _innerList.ConvertAll(func =>
            {
                Func<IMyType, bool> outFunc = type => func.Invoke((MyType<T>)type);
                return outFunc;
            });
            return output;
        }

        /// <summary>
        /// Add to list
        /// </summary>
        /// <param name="func"></param>
        public void AddToList(Func<MyType<T>, bool> func)
        {
            _innerList.Add(func);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            // get class / instance the uses said list
            var asClass = new ClassThatAccessesIt<ConcreteType>();
            var asInterface = (IFaceThatAccessesIt) asClass;

            // add into list (via class, not interface)
            asClass.AddToList(input => input.Number == 999);
            asClass.AddToList(input => input.Text == "Hello World");

            // get list from interface
            var listFromInterface = asInterface.GetList();

            // get function 1
            var func1 = listFromInterface.First();

            // invoke
            var inputInstance = new MyType<ConcreteType>();
            var result = func1.Invoke(inputInstance);

            // print
            Console.WriteLine("This result should be 'True'... {0}",result);

        }
    }
}
Community
  • 1
  • 1
LiverpoolsNumber9
  • 2,384
  • 3
  • 22
  • 34
  • This is still not type-safe though. When you call `GetList()[0]`, it returns a `Func`, but that function actually expects a `MyType` to be passed in, and if you pass in some other `IMyType` then it'll crash on line 42. – Dax Fohl Apr 23 '15 at 13:38
0

It's impossible to say what you need to do without knowing the larger scope of what you're doing, but what I'm guessing is for each object of IMyType you create you're wanting to add some function to call on it.

If that is the case, a typesafe way would be to hook them up in your AddToList, so you could keep a List<Func<bool>> as your member, taking MyType out of the signature completely:

public interface IFaceThatAccessesIt
{
    IList<Func<bool>> GetList();
}

public class ClassThatAccessesIt<T> : IFaceThatAccessesIt where T : SomeOtherType
{
    private readonly List<Func<bool>> _innerList = new List<Func<bool>>();

    public IList<Func<bool>> GetList()
    {
        return _innerList;
    }

    public void AddToList(MyType<T> thing, Func<MyType<T>, bool> func)
    {
        _innerList.Add(() => func(thing));
    }
}
Dax Fohl
  • 10,654
  • 6
  • 46
  • 90
  • Note if you go this way, if you don't have anything else in `ClassThatAccessesIt`, then that whole hierarchy becomes unnecesary overhead; you can just create the `List>` from your main code and pass that around to wherever you need it, without the fanfare, calling `list.Add(() => func(thing))` inline. Much simpler to understand this way. – Dax Fohl Apr 23 '15 at 14:37