【问题标题】:Does C# have an equivalent to Scala's structural typing?C# 是否具有等效于 Scala 的结构类型的功能?
【发布时间】:2011-02-19 08:16:40
【问题描述】:

在 Scala 中,我可以将structural types 定义如下:

type Pressable = { def press(): Unit }

这意味着我可以定义一个函数或方法,它以可按下的东西作为参数,如下所示:

def foo(i: Pressable) { // etc.

我传递给此函数的对象必须为它定义了一个名为 press() 的方法,该方法与类型中定义的类型签名相匹配 - 不接受任何参数,返回 Unit(Scala 的 void 版本)。

我什至可以使用内联结构类型:

def foo(i: { def press(): Unit }) { // etc.

它基本上允许程序员拥有鸭子类型的所有好处,同时仍然具有编译时类型检查的好处。

C# 有类似的东西吗?我在 Google 上搜索过,但找不到任何东西,但我对 C# 并不熟悉。如果没有,有没有计划添加这个?

【问题讨论】:

标签: c# scala duck-typing type-systems structural-typing


【解决方案1】:

没有,也没有我知道的计划。仅命名(而非结构)子类型(例如接口)。

(其他人可能也想看看

http://en.wikipedia.org/wiki/Nominative_type_system

http://en.wikipedia.org/wiki/Structural_type_system

)

(有些人可能会指出一些奇特的极端情况,例如 foreach 语句对 GetEnumerator 使用结构类型,但这是 exception 而不是规则。)

【讨论】:

  • 另一种情况是具有默认构造函数约束的泛型类型参数。
【解决方案2】:

没有办法定义具有特定功能的结构类型。有一个库可以为 C# 添加鸭子类型支持,可以在 here 找到。

这是来自 Duck Typing 项目的示例。请注意duck 输入发生在运行时并且可能会失败。我的理解也是,这个库为鸭子类型生成代理,这与 Scala 中优雅的编译时支持相去甚远。这很可能与这一代 C# 一样好。

public interface ICanAdd
{
    int Add(int x, int y);
}

// Note that MyAdder does NOT implement ICanAdd, 
// but it does define an Add method like the one in ICanAdd:
public class MyAdder
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}

public class Program
{
    void Main()
    {
        MyAdder myAdder = new MyAdder();

        // Even though ICanAdd is not implemented by MyAdder, 
        // we can duck cast it because it implements all the members:
        ICanAdd adder = DuckTyping.Cast<ICanAdd>(myAdder);

        // Now we can call adder as you would any ICanAdd object.
        // Transparently, this call is being forwarded to myAdder.
        int sum = adder.Add(2, 2);
    }
}

这是 C# 使用良好的无聊接口实现相同目标的方法。

interface IPressable {
  void Press();
}

class Foo {
 void Bar(IPressable pressable) {
    pressable.Press();
 }
}

class Thingy : IPressable, IPushable, etc {
 public void Press() {
 }
}

static class Program {
 public static void Main() {
  pressable = new Thingy();
  new Foo().Bar(pressable);
 }
}

【讨论】:

  • 如果方法不存在,duck-casting 库肯定会在运行时失败吗?只是想清楚这不是编译时检查。
  • 显然。我将添加一条粗体注释,说明它在运行时发生并且可能会失败。
【解决方案3】:

正如其他人所说,这在 .NET 中真正不可用(因为这更多的是运行时问题而不是语言问题)。但是,.NET 4.0 支持导入的 COM 接口类似的东西,我相信这可以用于实现 .NET 的结构类型。请参阅此博客文章:

我自己还没有尝试过使用它,但我认为它可能使编译器作者能够为 .NET 编写具有结构类型的语言。 (这个想法是您(或编译器)将在后台定义一个接口,但它会起作用,因为由于 COM 等效功能,这些接口将被视为等效。

此外,C# 4.0 支持 dynamic 关键字,我认为可以将其解释为结构类型(没有静态类型检查)。关键字允许您在不知道(在编译时)对象是否具有所需方法的情况下调用任何对象的方法。这与 Igor 提到的“Duck typing”项目本质上是一样的(但这当然不是正确的结构类型)。

【讨论】:

  • 这是一个糟糕的 hack,不应该使用!
  • 没有静态类型检查违背了结构类型的目的,IMO。
  • @Tomas:这(tomasp.net/blog/cannot-return-anonymous-type-from-method.aspx)不也是一种非常有限的穷人的结构类型吗?没有静态类型检查,也没有方法,但仍然如此。它当然不适用于OP的问题。
  • @Marucio:是的,我想是的。匿名类型使用结构类型,但仅限于本地。我猜如果它在全局范围内工作,那么它将是正确的结构类型,但这不能工作(因为来自不同程序集的两个等效匿名类型被视为不同类型 - 尽管.NET 4.0 中的 COM 技巧可能是一种解决方法:-))。
  • @musicfreak 静态类型和结构类型检查不是正交的——Scala 支持主格类型和结构类型(并且它是静态类型的)。见stackoverflow.com/questions/2831175/…
【解决方案4】:

C# 中的可等待模式或许可以解释为结构子类型/存在类型的有限、临时实例。编译器只会将await 对象访问到GetAwaiter() 方法,该方法返回任何具有一组特定方法和属性的INotifyCompletion 对象。由于 'awaitable' 对象和 'awaiter' 对象都不需要实现任何接口(在后者的情况下 INotifyCompletion 除外),await 类似于接受结构类型的可等待对象的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-07
    • 2012-07-18
    • 1970-01-01
    • 2011-03-31
    • 2012-03-21
    • 2011-06-13
    • 1970-01-01
    • 2020-07-21
    相关资源
    最近更新 更多