C#与默认方法vs traits的接口

C#与默认方法vs traits的接口,c#,scala,functional-programming,C#,Scala,Functional Programming,在Scala文档中,它给出了一个trait示例 trait Iterator[A] { def hasNext: Boolean def next(): A } class IntIterator(to: Int) extends Iterator[Int] { private var current = 0 override def hasNext: Boolean = current < to override def next(): Int = { if

在Scala文档中,它给出了一个trait示例

trait Iterator[A] {
  def hasNext: Boolean
  def next(): A
}

class IntIterator(to: Int) extends Iterator[Int] {
  private var current = 0
  override def hasNext: Boolean = current < to
  override def next(): Int = {
    if (hasNext) {
      val t = current
      current += 1
      t
    } else 0
  }
}


val iterator = new IntIterator(10)
iterator.next()  // returns 0
iterator.next()  // returns 1
trait迭代器[A]{
def hasNext:Boolean
def next():A
}
类IntIterator(to:Int)扩展了迭代器[Int]{
专用无功电流=0
覆盖def hasNext:Boolean=current
我们知道C#还不支持特质。但是,上面的示例可以很容易地转换为C代码:

接口迭代器
{ 
bool HasNext();
下一个();
}
公共类迭代器:迭代器
{
int_to;
int _电流=0;
公共计数器(int-to)=>\u-to=to;
公共bool HasNext()=>\u current<\u to;
public int Next()=>HasNext()?_current++:0;
}
变量寄存器=新的计数器(10);
下一个
C#接口现在可以有默认方法。与特质相比,C#缺少什么


或者应该有一个更好的Scala例子来展示特质的力量?

特别是与Scala特质相比(不同语言中的特质可能非常不同),C#缺失了:

  • 初始化代码:您可以

    trait A {
      println("A's constructor")
    }
    
    并且该代码将在继承自
    A
    的任何类的构造函数中执行(以正确的顺序)。或者更简单地说

    trait A {
      val x = 10
    }
    
  • (至少在具体细节中)

  • 不同的
    base
    (在C#)中)/
    super
    (在Scala中)分辨率,这意味着该模式无法与C#接口一起工作

  • (来自Scala 3)

  • (在Scala 3中删除)


  • 从技术上讲,你可以在C#中做一些类似于特质的事情。特征是特殊的多态性。i、 e.你可以在礼物类型上添加额外的特质,即席或事后。当您得到一个类型(如BCL中的
    IEnumerable
    )并且希望它具有附加属性(如作为单子)时,这非常有用(但您不拥有该类型,因此无法更改其接口列表)

    因此,对于迭代器示例,您可以这样做:

    public interface Iterator<MA, A>
    {
        bool HasNext(MA iter);
        A Next(MA iter);
    }
    
    忽略
    MoveNext
    /
    HasNext
    ,而
    Next
    /
    Current
    并不意味着相同的事情,这只是为了方便

    请注意类型是如何成为结构的。这是因为结构永远不能为
    null
    。因此,
    default(MY_STRUCT)
    将始终具有非空值

    现在,我们将创建一个通用实现,它使用
    迭代器
    ,对底层类型一无所知:

    public static IEnumerable<A> IterAnything<IterA, MA, A>(MA iter) 
        where IterA : struct, Iterator<MA, A>
    {
        while(default(IterA).HasNext(iter))
        {
            yield return default(IterA).Next(iter);
        }
    }
    
    这种方法的一个简单示例是
    Num(称为类型类和类实例)

    如果.NET团队制作了一个
    INumeric
    接口并使所有数字类型都从中派生出来,那么使用它将导致装箱。上面演示的方法根本不会导致任何拳击。此外,所有的
    default(NumA)
    调用在发布版本中都得到了优化,因此它与直接调用函数一样快


    局限性在于C#没有更高的种类,这会导致在尝试实现诸如
    Monad
    Functor
    之类的东西时出现问题,但是@Renat不,它们不是。
    public struct IteratorEnum<A> : Iterator<IEnumerator<A>, A>
    {
        public bool HasNext(IEnumerator<A> iter) =>
            iter.MoveNext();
    
        public A Next(IEnumerator<A> iter) =>
            iter.Current;
    }
    
    public static IEnumerable<A> IterAnything<IterA, MA, A>(MA iter) 
        where IterA : struct, Iterator<MA, A>
    {
        while(default(IterA).HasNext(iter))
        {
            yield return default(IterA).Next(iter);
        }
    }
    
    var items = (new[] { 1, 2, 3, 4, 5 }).AsEnumerable().GetEnumerator();
    
    var newitems = IterAnything<IteratorEnum<int>, IEnumerator<int>, int>(items);
    
    public interface Num<A>
    {
        A Add(A lhs, A rhs);
        A Subtract(A lhs, A rhs);
        A Multiply(A lhs, A rhs);
        A Divide(A lhs, A rhs);
        A FromInt(int value);
        A One { get; }
        A Zero { get; }
    }
    
    public interface Eq<A>
    {
        bool IsEqualTo(A lhs, A rhs);
    
        // using C#8 default interface methods
        bool IsNoEqualTo(A lhs, A rhs) => !IsEqualTo(lhs, rhs); 
    }
    
    public struct NumInt : Num<int>, Eq<int>
    {
        public int Add(int lhs, int rhs) => lhs + rhs;
        public int Subtract(int lhs, int rhs) => lhs - rhs;
        public int Multiply(int lhs, int rhs) => lhs * rhs;
        public int Divide(int lhs, int rhs) => lhs / rhs;
        public int FromInt(int value) => value;
        public bool IsEqualTo(int lhs, int rhs) => lhs == rhs;
        public int One => 1;
        public int Zero => 0;
    }
    
    public struct NumLong : Num<long>, Eq<long>
    {
        public long Add(long lhs, long rhs) => lhs + rhs;
        public long Subtract(long lhs, long rhs) => lhs - rhs;
        public long Multiply(long lhs, long rhs) => lhs * rhs;
        public long Divide(long lhs, long rhs) => lhs / rhs;
        public long FromInt(int value) => (long)value;
        public bool IsEqualTo(long lhs, long rhs) => lhs == rhs;
        public long One => 1;
        public long Zero => 0;
    }
    
    public static bool IsEqualTo0<NumA, A>(A n) where NumA : struct, Num<A>, Eq<A> =>
        default(NumA).IsEqualTo(n, default(NumA).Zero);
    
    public static bool IsEqualTo1<NumA, A>(A n) where NumA : struct, Num<A>, Eq<A> =>
        default(NumA).IsEqualTo(n, default(NumA).One);
    
    public static A Subtract1<NumA, A>(A n) where NumA : struct, Num<A>, Eq<A> =>
        default(NumA).Subtract(n, default(NumA).One);
    
    public static A Subtract2<NumA, A>(A n) where NumA : struct, Num<A>, Eq<A> =>
        Subtract1<NumA, A>(Subtract1<NumA, A>(n));
    
    public static A Fibonacci<NumA, A>(A n) where NumA : struct, Num<A>, Eq<A> =>
        IsEqualTo0<NumA, A>(n) || IsEqualTo1<NumA, A>(n)
            ? n
            : Fibonacci<NumA, A>(default(NumA).Add(
                                    Subtract1<NumA, A>(n), 
                                    Subtract2<NumA, A>(n)));
    
    Fibonacci<NumInt, int>(100);
    Fibonacci<NumLong, long>(100L);