什么是界面鸭打字?

时间:2014-01-22 08:46:45

标签: c# oop

我听到了接口鸭打字这个词,但根本不明白它是什么?所以我读了一篇关于这个的维基,他们说:

  

在使用面向对象编程语言的计算机编程中,duck typing是一种打字方式,其中对象的方法和属性决定了有效的语义,而不是从特定类或显式接口的实现继承。概念的名称是指鸭子测试。

但仍然无法理解它是什么。所以我看到了他们的计划,但他们使用dynamic关键字来呼叫quack()&所有类的feather()函数。

我会请求大家请简单解释什么是Interface Duck Typing以及如何在C#v2.0中实现,因为没有dynamic关键字。

using System;

namespace DuckTyping 
{  
  public class Duck 
  {
    public void Quack() 
    {
      Console.WriteLine("Quaaaaaack!");
    }

    public void Feathers() 
    {
      Console.WriteLine("The duck has white and gray feathers.");
    }
  }

  public class Person 
  {
    public void Quack()
    {
      Console.WriteLine("The person imitates a duck.");
    }

    public void Feathers() 
    {
      Console.WriteLine("The person takes a feather from the ground and shows it.");
    }
  }

  internal class Program 
  {
    private static void InTheForest(dynamic duck) 
    {
      duck.Quack();
      duck.Feathers();
    }

    private static void Game() 
    {
      Duck donald = new Duck();
      Person john = new Person();
      InTheForest(donald);
      InTheForest(john);
    }

    private static void Main() 
    {
      Game();
    }
  }
}

5 个答案:

答案 0 :(得分:21)

C#具有标称类型系统,因此类型的兼容性基于其名称完成。在您的示例中,您有两个带有Quack方法的类,但是无法编写可以接受这两个类的实例并调用其Quack方法的方法。

在C#2中,解决方案是引入一个接口并让两个类实现它:

public interface IQuack
{
    void Quack();
}

public class Duck : IQuack { }
public class Human : IQuack { }

现在您可以创建一个采用IQuack实例的方法,并可以通过它调用Human.QuackDuck.Quack。在C#中,方法在编译时“早期”解析,因此您需要创建一个支持该方法所需操作的命名类型,以便编译成功。注意,仍然有一个运行时元素来调用这些方法,因为IQuack.Quack的实际实现需要在运行时根据参数的实际类型进行解析。

在鸭子类型系统中,没有尝试在运行时之前验证方法是否存在。所需要的只是给定的对象支持操作,因为它具有正确的名称并且获取所需数量的参数(在这种情况下为none),因此'如果它像鸭子一样嘎嘎叫'。

在C#2中输入鸭子只能使用反射来完成,在这种情况下,你会接受一个object参数并自己寻找所需的方法:

public static void MakeQuack(object duck)
{
    MethodInfo quackMethod = duck.GetType().GetMethod("Quack", Type.EmptyTypes, null);
    if (quackMethod!=null)
    {
        quackMethod.Invoke(duck, new object[] { });
    }
    else
    {
        throw new ArgumentException("No Quack() method found on target");
    }
}

C#4使dynamic

更加简单
public static void MakeQuack(dynamic duck)
{
    duck.Quack();
}

答案 1 :(得分:12)

它会说这是一种编码方式,你告诉编译器:

“嘿,相信我,我知道这个对象支持哪些方法和属性。在编写代码时,你不需要为我检查它们。”

运行应用后,编译器将会:  “好的,看看我是否可以信任你。让我做一些运行时绑定。”

如果你犯了一个错误,比如使用不支持的方法,编译器就会喊:“嘿,伙计,这是不受支持的!检查我的RuntimeBinder异常!”

答案 2 :(得分:8)

  

Duck输入允许将对象传递给期望的方法   某种类型,即使它不从该类型继承。一切都有   要做的是支持预期类型的​​方法和属性   通过该方法使用。我强调最后一句是有原因的。假设   我们有一个接受duck实例的方法,另一个方法   这需要一个兔子实例。在动态类型语言中   支持duck typing,我可以将我的对象传递给第一个方法as   只要我的对象支持使用中的duck的方法和属性   通过那种方法。同样,我可以将我的对象传递给第二种方法   只要它支持被称为兔子的方法和属性   第二种方法。我的对象是鸭子还是兔子?像   上面的图像,它既不是,也是两者。在许多(如果不是大多数)动态   语言,我的对象不必支持所有方法和   要传递给期望鸭子的方法的鸭子的属性。   对于需要兔子的方法也是如此。它只需要支持   实际的预期类型的​​方法和属性   通过该方法调用。

请参阅此内容以了解Duck Typing

http://haacked.com/archive/2007/08/19/why-duck-typing-matters-to-c-developers.aspx/

答案 3 :(得分:3)

关于Duck打字:

  

我们不需要知道对象是什么,但我们只想让它   如果可以,对象会做一些事情。

示例:

示例,如果以下是我们想要以下对象的内容。

PleaseWalk(new Dog());
PleaseRun(new Duck());
PleaseWalk(new Cup());
PleaseFly(new Man());
PleaseFly(new Bird());

而且,这是我们请求上述对象后的结果。 enter image description here

因此,我们不需要检查对象是什么,但我们可以让它做得足够多。这是我用C#编写的代码。

private void PleaseWalk(object obj)
    {
        string Method = "Walk";
        MethodInfo walkMethod = obj.GetType().GetMethod(Method, Type.EmptyTypes, null);
        if (walkMethod != null)
        {
            walkMethod.Invoke(obj, new object[] { });
        }
        else
        {
            Console.WriteLine(string.Format("I can not {0} because {1}", Method, WhoAreYou(obj)));
        }
    }

    private string WhoAreYou(object unknown)
    {
        MethodInfo whoAreYou = unknown.GetType().GetMethod("WhoAreYou", Type.EmptyTypes, null);
        return whoAreYou.Invoke(unknown, new object[] { }).ToString();
    }

答案 4 :(得分:0)

@Ewald Stieger @Patrick Fromberg @Lee @John Pluto解决方案@tarzanbappa @Thomas @BartoszKP您可以使用事件并利用C#最合适的重载函数。 希望它将是有用的:) 要获得像鸭子打字(.Net 4. +)这样的东西:

using System.Collections;
using System.Collections.Generic;
public interface  IAny
{
    void InvokeGetterEvent();
}
 public class AnyValueTypeDuck<T, V> : IAny
    where V : AnyValueTypeDuck<T, V>
 {
    public static event System.Action<V> GetterEvent;
    public T Data;
    public void InvokeGetterEvent()
    {
        GetterEvent.Invoke((V)this);
    }
 }

// Then create some concrete classes:
// Example :
public class LifeConcreteProperty : AnyValueTypeDuck<int, LifeConcreteProperty>
{
}
public class ManaConcreteProperty : AnyValueTypeDuck<float, ManaConcreteProperty>
{
}

// Now to finally use it :
public class UserClass
{
    List<IAny> allDuckTypes = new List<IAny>();

    public void GetDucketTypeClass(IAny anyDuckObject)
    {
        LifeConcreteProperty.GetterEvent += GetDucketType;
        ManaConcreteProperty.GetterEvent += GetDucketType;
        anyDuckObject.InvokeGetterEvent();
        // it will propagate to event and will invoke 
        // best suitable overload method (GetDucketType)
        LifeConcreteProperty.GetterEvent -= GetDucketType;
        ManaConcreteProperty.GetterEvent -= GetDucketType;
    }

    public void GetDucketType(LifeConcreteProperty originalClass)
    {
        // Your efforts go here
        int value =  originalClass.Data;
    }
    public void GetDucketType(ManaConcreteProperty originalClass)
    {
        // Your efforts go here
        float value =  originalClass.Data;
    }
}