如何将函数用作变量?

时间:2014-09-11 15:22:38

标签: c# javascript

所以我习惯使用,只能做

dog={};
dog.name="Rex";
dog.examine=function(){console.log("This is a dog. Its name is "+this.name+".");}
cat={};
cat.name="Phil Collins";
cat.examine=function(){console.log("This is a cat. Its name is "+this.name+".");}

dog.examine();
cat.examine();

当然会返回: This is a dog. Its name is Rex. This is a cat. Its name is Phil Collins.

我开始学习,我对整个lambda / delegate / action系统感到非常困惑。我不确定使用什么语法,编译器在我尝试创建没有输入或输出变量的函数时会生气,而且我无法保持 this 。有人可以帮我看看我如何将上述代码移植到

5 个答案:

答案 0 :(得分:4)

我将其设为社区Wiki,因为这个问题实在太宽泛,C#和JavaScript是非常不同的东西,答案也不完整。我只是简单地描绘一个方向,但是要走的路是学习C#,差异很明显。这就是为什么我会尝试首先将类似写入您发布的JavaScript程序,然后逐步将其更改为更多C#样式。

我还要说,如果你非常想在C#(或JavaScript编程风格)中使用动态类型,那么(可能)......你选择了错误的语言。

第1步

某种接近你所写的东西可以用C#写成这样的(让我用这个例子来强调差异,你真的必须买一本好的C#书并从那里开始):

dynamic dog = new ExpandoObject();
dog.Name = "Pluto";
dog.Hello = new Action(() => Console.WriteLine("This is a dog named {0}", dog.Name));
dog.Hello();
  1. 首先你要看到在C#中必须输入一个变量,在这种情况下使用dynamic我们绕过静态类型,我们甚至可以在以后更改内容(但这是另一个故事)。 ExpandoObject是一个特殊对象,可以扩展添加属性,它不是普通 C#行为,其中几乎所有关于类型的内容都在编译时检查(不要想想演员,现在)。
  2. 如果你是传统的 C#程序员,那么第二行非常相似,对你来说并不新鲜(对你而言),但非常奇怪
  3. 最后有趣的部分:我们添加一个属性作为委托(使用lambda)但是这里有一个很大的区别(你自己也注意到了):this在C#中有不同的含义方法this是声明方法的对象的实例(确定它在类中声明,但你知道我的意思)。让我们看一下:dog.Name,我们在我们的匿名方法中捕获 dog变量(就像在JavaScript中一样)。
  4. 第2步

    这只是一个起点,因为设计和哲学是完全不同的,C#中的相同内容应该使用Animal基类和Dog + Cat派生类来完成,但是你会自己学习。让我在这方面做一个简单的步骤:

    var typedDog = new {
        Name = "Pluto",
        Hello = new Action(() => Console.WriteLine("This is a dog named {0}", Name))
    };
    typedDog.Hello();
    

    也许你没有看到如此大的差异,但这个代码是强类型的!这是什么意思?我们声明了一个带有两个属性的匿名类,其中一个是委托,我们仍然不能使用this而在C#中(与Java不同)我们不能在匿名类型中声明方法,但现在编译器知道了(那么它是什么东西都是编译时间。例如:

    dog.Name = 2; // Valid, now Name is an integer
    dog.Hello = 2; // Valid, also Hello is an integer
    dog.Hello(); // This will fail at run-time because Hello now isn't a delegate
    

    不好吗,对吧?使用我们的新类型对象,这是不可能的:

    typedDog.Name = 2; // Compile-time error, Name is a string
    typedDog.Hello = 2; // Compile-time error, Hello must be an Action delegate
    

    当然我们可以指定一个新的匿名委托来替换旧的(但类型必须匹配):

    typedDog.Hello = new Action(() => Console.WriteLine("This is a typed dog named {0}", typedDog.Name));
    

    第3步

    这已经在其他答案中得到了广泛的描述,所以我不再重复,只是为了草拟一些事情:

    class Animal {
        public string Name { get; set; }
        public abstract void Hello();
    }
    
    class Dog : Animal {
        public override void Hello() {
            Console.WriteLine("This is a dog named {0}", this.Name);
        }
    }
    

    请注意,现在您终于拥有了this指针,它可以满足您的期望。它的使用方式如下:

    var dog = new Dog { Name = "Pluto" };
    dog.Hello();
    

    请注意,在JavaScript中,您甚至可以写下这个:

    var anInteger = 2;
    anInteger.PrintSomething();
    

    在C#中不允许这样做,因为在编译时(除非你使用dynamic个变量),它需要知道PrintSomething()是否是一种方法以及如何调用它。同样的事情也可以这样做(使用接口):

    class IPolite {
        void Hello();
    }
    
    class Dog : IPolite {
        public string Name { get; set; }
        public void Hello() {
            Console.WriteLine("This is a dog named {0}", this.Name);
        }
    }
    

    现在你甚至可以拥有一个完全不同的对象:

    class Car : IPolite {
        public string Name { get; set; }
        public void Hello() {
            Console.WriteLine("This is a car, name is {0}", this.Name);
        }
    }
    

    可以像这样使用:

    IPolite thing = new Dog { Name = "Pluto" };
    thing.Hello();
    
    thing = new Car { Name = "Ferrari F50" };
    thing.Hello();
    

    请注意我们正在重复使用相同的thing变量。许多其他事情要看和做...

答案 1 :(得分:2)

通常,C#和其他强类型语言与JS等脚本/运行时语言完全不同。 C#是一种编译语言,编译器确保了这种“强类型”特性。对于许多类型安全(http://en.wikipedia.org/wiki/Type_safety)语言都是如此。

一般来说,C#中的类结构如下所示:

public abstract class Animal {

    //Fields or instance variables are typically hidden from the outside world (consuming code).  This is controlled by the 'access-modifier' in this case, private.
    private string _name;

    //Constructor is called when you use the 'new' keyword to instantiate an instance of a type that derives from Animal (Animal cannot be instantiated directly because it is abstract).
    protected Animal() {
        //Avoids null references unless someone overrides the property setter, for this example, it's safe enough
        _name = string.Empty;
    }

    //This is syntax for declaring a property
    //properties are publicly accessible pieces of data that control access to a basic
    // field (variable).
    // It allows you to apply logic to the field it wraps.
    // In this example, the field cannot be set to a null or empty string (except by the constructor, which bypasses the property.
    public virtual string Name {
        get { 
            return _name;
        } set {
            if(!String.IsNullOrWhiteSpace(value)) {
                _name = value;
            }
        }
    } // end property Name

    //This is a method that must be overridden by any derived type that is not abstract and may (or may not) be overridden by a derived type that is abstract.
    public abstract void Examine();
}

public class Cat : Animal {
    public Cat : base() {}
    public override void Examine() {
        Console.WriteLine(String.Concat("This is a cat.  It's name is ", this.Name, "."));
    }
}

public Class Dog : Animal {
    public Dog() : base() {}
    public override void Examine() {
        Console.WriteLine(String.Concat("This is a dog.  It's name is ", this.Name, "."));
    }
}

//In some runnable code elsewhere like a console application:

Animal cat = new Cat() {Name = "Mittens"};
Animal dog = new Dog() {Name = "Fido"};
cat.Examine();
dog.Examine();

答案 2 :(得分:0)

您可以使用继承来完成它:

public class Animal
{
    public string Name { get; private set; }

    public Animal(string name)
    {
        this.Name = name;
    }

    public void Examine()
    {
        Console.WriteLine("This is a {0}. Its name is {1}.", this.GetType(), Name);
    }
}

public void Dog : Animal
{
    public Dog(string name) : base(name) { }
}

public void Cat : Animal
{
    public Cat(string name) : base(name) { }
}

然后您可以创建这些派生类型的实例:

static class Main(string[] args)
{
    Dog rex = new Dog("rex");
    Cat phil = new Cat("Phil Collins");

    rex.Examine();
    phil.Examine();
}

答案 3 :(得分:0)

这是一个简单的例子。我强烈建议你拿一本书或者查看一个教程,因为这些东西很早就会被提及。

public abstract class Animal
{
    public string Type { get; private set; }
    public string Name { get; set; }

    protected Animal(string type)
    {
        Type = type;
    }

    public virtual string Examine()
    {
        return string.Format("This is a {0}. Its name is {1}.", Type, Name);
    }
}

public class Dog : Animal
{
    public Dog() : base("Dog")
    {
    }
}

public class Cat : Animal
{
    public Cat() : base("Cat")
    {
    }
}

var dog = new Dog { Name = "Rex" };
var cat = new Cat { Name = "Phil Collins" };

答案 4 :(得分:0)

您可以使用所谓的匿名函数,也可以将您的此函数作为Action类型的属性。例如,你可以写:

Animal.cs:

public abstract class Animal
{
    public string Name { get; set; }
    public Action Examine { get; set;}
}

Dog.cs:

public class Dog : Animal
{

}

Cat.cs:

public class Cat : Animal
{

}

然后,在某个你可以使用它的地方,你可以说:

Dog dog = new Dog { Name = "Rex" };
dog.Examine = delegate
{
    Console.WriteLine("This is a dog. Its name is {0}.", dog.Name);
};

Cat cat = new Cat { Name = "Phil Collins" };
cat.Examine = delegate
{
    Console.WriteLine("This is a cat. Its name is {0}.", cat.Name);
};

dog.Examine();
cat.Examine();

请记住,不是使用'this',而是使用对以前实例化的类的引用,该类扩展了Animal(Dog或Cat)。

还有另一个选项......组合ExpandoObject类和动态关键字:

dynamic dog = new ExpandoObject();
dog.Name = "Rex";

Action examineDog = delegate {
    Console.WriteLine("This is a dog. Its name is {0}.", dog.Name);
};

dog.Examine = examineDog;

dynamic cat = new ExpandoObject();
cat.Name = "Phil Collins";

Action examineCat = delegate
{
    Console.WriteLine("This is a cat. Its name is {0}.", cat.Name);
};

cat.Examine = examineCat;

dog.Examine();
cat.Examine();