从函数类型参数中的泛型类型转换(错误)

时间:2015-04-22 23:48:48

标签: c# generics casting

我遇到的问题是,在下面的示例中,我可以将IMyType转换为Func<MyType<T>, bool>,很好。但是,我无法将Func<IMyType, bool>转换为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...) } }

这没有任何意义,我想知道是否有办法绕过它可能不同的架构?

我不想使用反射。

我也想了解它失败的原因。

.NET 4.5.2中的代码失败。此处的在线版本 - http://goo.gl/13z4xg - 也以同样的方式失败。

position = PositiveIntegerField(unique=True)

3 个答案:

答案 0 :(得分:1)

抱歉......我认为各种答案很好地解释了为什么你不想这样做。即使你认为你做了(一个常见的错误,所以没有理由对此感到不好)。我没有想到,在阅读了所有这些问题及其答案之后,你不会觉得你对自己的问题有足够的答案。我不确定我能为他们增添多少,但我会尝试......


简短版本:如果您被允许将Func<MyType<ConcreteType>, bool>的实例添加到Func<IMyType, bool>列表中,那么其他一些代码可以将该实例作为Func<IMyType, bool>拉出,然后尝试调用它,向代理人传递IMyType以外的MyType<ConcreteType>实例。

但是,您的方法(可能是给定委托声明)只需要MyType<ConcreteType>的实例!如果它通过IMyType的其他实现?

会怎么做?


当编译器list.Add(func)给出编译时错误时,编译器尝试以帮助防止您犯错误。您使用强制转换器隐藏了编译器中的错误,但这并没有改变编译器试图保存您的基本危险,因此当您尝试强制转换并在那里停止时,运行时必须介入。< / p>

最好在编译时阻止代码,但是当你使用强制转换来声明你对编译器没有的类型有所了解时,这是不可能的。但是,当你试图将IMyType的其他实现传递给委托所代表的方法时,在演员阵容中获得运行时错误仍然会更好。


现在,你给出的代码示例并不清楚为什么你认为这甚至是有利的。所以在你的情况下,不可能知道哪种替代方案最好。

如果委托引用的方法确实需要知道类型是MyType<ConcreteType>,那么你就被卡住了。将代理引用该方法放在任何IMyType可以传递给它的上下文中是不安全的。你以某种方式把自己描绘成一个角落,你需要重新考虑你的设计,因为它目前有一个根本的缺陷。

但是,请注意,如果确实仅在方法中使用对象的IMyType - 那么解决方案就是以这种方式声明它。例如,如果将您显示的lambda表达式转换为类型为Func<IMyType, bool>的变量,则 可以添加到列表中。

或者,如果您<100>确定没有代码会尝试使用除IMyType以外的MyType<ConcreteType>实现来调用委托,则可以包装错误委托类型在右代理类型的实例中。例如:

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

当然,通过这样做,您将完全删除使用通用List<T>对象的大量类型安全功能。它绝对有“糟糕的代码味道”。但它会编译,甚至可以运行,只要你从不违反规则并尝试使用IMyType的不同实现来调用委托。

答案 1 :(得分:0)

回答我自己的问题,或者至少是&#34;所以我怎样才能让它发挥作用&#34;一部分。

问题是差异(请参阅评论中的重复项)。在这种情况下答案是&#34;我怎样才能使它工作&#34;部分是使用List<T>.ConvertAll(如此处所建议的 - Jon Skeet的C# variance problem: Assigning List<Derived> as List<Base>)。

对于我的需要,这个解决方案是完美的。 &#34;费用&#34;在我的情况下创建新列表并不重要

所以我已经完成了对示例的扩展,并使用List<T>.ConvertAll以便非泛型感知实例可以访问这些函数。此处链接到代码 - http://goo.gl/MVnYhX - 代码也位于以下位置:

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);

        }
    }
}

答案 2 :(得分:0)

如果不知道你所做的事情的范围越广,就无法说出你需要做什么,但我猜测的是你创造的IMyType的每个对象。你想要添加一些函数来调用它。

如果是这种情况,那么类型安全的方式就是将它们挂钩到AddToList,这样您就可以保留List<Func<bool>>作为您的会员,从签名中取出MyType完全:

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));
    }
}