接口,继承,隐式运算符和类型转换,为什么会这样?

时间:2015-08-26 12:35:58

标签: c# linq interface implicit-conversion data-conversion

我正在使用名为DDay ICal的类库。 它是在Outlook日历中实现的iCalendar系统的C#包装器,以及许多更多系统。 我的问题来源于我对这个系统所做的一些工作。

这里有3个问题

  • IRecurrencePattern - Interface
  • RecurrencePattern - IRecurrencePattern接口的实现
  • DbRecurPatt - 具有隐式类型运算符的自定义类

IRecurrencePattern:并非所有代码都显示

public interface IRecurrencePattern
{
    string Data { get; set; }
}

RecurrencePattern:并非所有代码都显示

public class RecurrencePattern : IRecurrencePattern
{
    public string Data { get; set; }
}

DbRecurPatt:并非所有代码都显示

public class DbRecurPatt
{
    public string Name { get; set; }
    public string Description { get; set; }

    public static implicit operator RecurrencePattern(DbRecurPatt obj)
    {
        return new RecurrencePattern() { Data = $"{Name} - {Description}" };
    }
}

令人困惑的部分:通过DDay.ICal系统,他们使用IList来包含日历中每个事件的重复模式的集合,自定义类用于从数据库中获取信息然后它通过隐式类型转换运算符强制转换为重复模式。

但在代码中,我注意到从List<IRecurrencePattern>转换为List<DbRecurPatt>后它一直崩溃我意识到我需要转换为RecurrencePattern,然后转换为{{1} (因为还有其他以不同方式实现IRecurrencePattern的类也包含在集合

IRecurrencePattern

上述代码不起作用,它会在var unsorted = new List<DbRecurPatt>{ new DbRecurPatt(), new DbRecurPatt() }; var sorted = unsorted.Select(t => (IRecurrencePattern)t); 上抛出错误。

IRecurrencePattern

这确实有效,所以我的问题是;为什么第一个不起作用? (有没有办法改进这种方法?)

我相信这可能是因为隐式运算符在var sorted = unsorted.Select(t => (IRecurrencePattern)(RecurrencePattern)t); 对象而不是接口上,这是正确的吗? (我是接口和隐式运算符的新手)

4 个答案:

答案 0 :(得分:16)

您基本上已经要求编译器执行此操作:

  1. 我有:@Entity @Table(name = "FOO", schema = "dbo", catalog = "BAR_DEV") public class Foo{ /* ... */ }
  2. 我想要这个:DbRecurPatt
  3. 请找出从第1点到第2点的方法。
  4. 尽管编译器可能只有一个选项,但它不允许您这样做。演员操作员明确指出IRecurrencePattern可以转换为DbRecurPatt,而不是RecurrencePattern

    编译器只检查所涉及的两种类型中的一种是否指定了如何从一种转换为另一种的规则,它不允许中间步骤。

    由于没有定义允许IRecurrencePattern直接转换为DbRecurPatt的运算符,编译器会将其编译为硬转换,通过接口将引用重新解释为引用,这将失败在运行时。

    所以,接下来的问题是:我怎么能这样做呢?答案是你不能。

    编译器不允许您在接口上定义用户定义的转换运算符。 A different question here on Stack Overflow has more information

    如果您尝试定义此类运算符:

    IRecurrencePattern

    编译器会这样说:

      

    CS0552
      'DbRecurPatt.implicit operator IRecurrencePattern(DbRecurPatt)':不允许用户定义到接口或来自接口的转换

答案 1 :(得分:6)

  

为什么第一个不起作用?

因为您要求运行时进行两个隐式转换 - 一个到RecurrencePattern,一个到IRecurrencePattern。运行时只会查找直接隐式关系 - 它不会扫描所有可能的路由以让您去询问它。假设有多个隐式转换到实现IRecurrencePattern的不同类型的类。运行时选择哪一个?相反,它会强制您指定单独的强制转换。

这在C#语言规范的第6.4.3节中有记录:

  

评估用户定义的转换从不涉及多个   用户定义或提升的转换运算符。换句话说,一个   从类型S到类型T的转换永远不会首先执行a   用户定义的从S到X的转换,然后执行用户定义的   从X转换为T.

答案 2 :(得分:2)

还有另一条路可以实现你想要的。特别是在方法调用上标记您的泛型参数,而不是让编译器推断您的泛型参数。你仍然会避免施放,并且它可能比其他一些选项稍微冗长一些。唯一需要注意的是,如果重要的话,你必须包含一个额外的Linq声明,它将解析你的清单。

var sorted = unsorted
   .Select<DbRecurPatt, RecurrencePattern>(t => t)
   .ToList<IRecurrencePattern>();

您也可以将此答案与sstan相结合,以避免额外的Linq声明。

答案 3 :(得分:1)

...并回答关于隐式运算符的最后一个问题 - 不,你不能在接口上定义隐式运算符。这个问题在这个问题中有更详细的介绍:

implicit operator using interfaces