C#类与结构

本文介绍C#中类的创建和应用,其中,类的成员类型包括属性、方法、构造函数和析构函数、索引器等;然后介绍了静态类和静态方法,以及扩展方法的应用;最后,介绍了空值、var关键字和匿名类型、使用Type对象获取类型信息、Lambda表达式和结构。

类与对象

前面的示例中已经创建了C1类,下面继续修改C1类的代码(/cfx/demo/C1.cs)。

C#
using System;

namespace cfx.demo
{
    public class C1
    {
        //
        public string Name = "";
        //
        public void SayHello()
        {
            Console.WriteLine("Hello,{0}.", Name);
        }
        //
        public uint Age { get; set; }
    }
}

代码中,定义了C1类的三个成员,分别是:

  • Name字段,直接使用string类型的变量定义。
  • Age属性,这里使用了最简单的属性定义,称为自动属性。Age定义为可以读、写的属性,其中,get语句属性的读取操作,set语句定义属性的写入操作。
  • SayHello()方法定义为没有参数,没有返回值(void),其功能是显示"Hello"和Name字段的内容。

代码中的public表示成员的访问级别,C#中的访问级别包括:

  • public,公共的,即在类的内部和外部都可以调用。
  • internal,与public相似,但只能在其定义的程序集中使用。
  • protected,受保护的,只能在类的内部和其子类中调用,关于子类和继承会在后续文章中讨论。
  • private,私有的,只在当前类中调用。
  • protected internal,与protected相似,但只能在其定义的程序集的子类中使用。

此外,这三个成员都是实例成员,可以使用new关键字创建实例,然后由实例调用。下面的代码,在Program.cs中测试C1类,代码会创建C1类的实例,并赋值给obj1对象,然后通过obj1对象调用实例成员。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1();
            obj1.Name = "Tom";
            obj1.Age = 25;
            obj1.SayHello();  // Hello,Tom.
            Console.WriteLine(obj1.Age);  // 25
        }
    }
}

从代码应用的形式上看,字段和属性似乎区别并不大,但需要注意,字段是变量(对象)定义的,只能进行简单的数据读写操作,而属性则可以通过get和set语句块进行更多的读、写控制。

属性的读写

下面的代码会修改C1类中Age属性的定义。

C#
using System;

namespace cfx.demo
{
    public class C1
    {
        //
        public string Name = "";
        //
        public void SayHello()
        {
            Console.WriteLine("Hello,{0}.", Name);
        }
        //
        private uint myAge=0;
        public uint Age
        {
            get 
            {
                return myAge;
            }
            set
            {
                if (value > 150)
                    throw new Exception("年龄一般不会大于150岁");
                else
                    myAge = value;
            }
        }
    }
}

代码中,新增了私有(private)变量myAge保存年龄的属性值,默认值为0;然后,在get块中直接返回myAge变量的数据,即Age属性的值;set块中,使用value关键字读取设置的属性值,这里,当设置的属性值大于150时会抛出一个异常(Exception)对象,小于150时将value的值保存到myAge变量中;请注意,Age属性的数据类型定义为uint,只能处理大于等于0的整数,所以,当赋值为小于0的数据时无法通过编译,如果设置为有符号的数值类型,则在set语句块中还应处理属性值小于0的情况。

下面的代码,在Program.cs文件中测试Age属性的读、写操作。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1();
            obj1.Age = 25;
            Console.WriteLine(obj1.Age);  // 25
            obj1.Age = 200; // 抛出异常
        }
    }
}

代码执行结果如下图所示。

属性的读写测试

如果属性值只允许读取,即只读属性,可以只设置get语句块,不设置set语句块(可通过变量保存数据)或者将set语句块设置为private;只读属性一般在实例化时指定数据或通过计算返回数据;在类的外部只能读取,不能修改。

同样的,如果属性值只允许写入,不允许读取,即只写属性,则只设置set语句块,不设置get语句块;只写属性一般用于设置辅助数据。

方法与重载

方法定义的基本要素包括修饰符、方法名称、参数、返回值类型和方法体,格式如下:

C#
<修饰符> <返回值类型> <方法名称>(<参数>)
{
    <方法体>
}

其中:

  • <修饰符>中可以指定访问级别,如关键字public、private、protected、interval,可以使用static关键字设置为静态方法;还可以使用sealed、override、new等关键字,后续文章会有详细介绍。
  • <返回值类型>是指方法返回数据的类型,如果没有返回值则指定为void。
  • <方法名称>,即调用方法的名称,一般使用单词首字母大写的格式。
  • <参数>可以为空,也可以是一个或多个参数,多个参数使用逗号分隔。参数的设置比较灵活,除了普通的参数,还可以指定参数的默认值,或者使用ref关键字设置为按引用传递的参数,使用out关键字设置为输出参数,使用params关键字指定为参数数组。
  • <方法体>定义方法的执行代码,其中可以使用return语句返回数据。

下面的代码,在Program.cs文件中进行测试。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Add(10, 99)); // 109
            Console.WriteLine(Add(10)); // 10
        }
        //
        static int Add(int x,int y = 0)
        {
            return x + y;
        }
    }
}

代码中,除了Main()主方法,还定义了Add()方法,其功能是返回两个参数的和,其中,x参数定义为int类型,没有默认值,调用时必须指定数据;y参数同样定义为int类型,并指定的了默认值0。在Main()方法中,调用了两个Add()方法,第一次设置参数为10和99,返回结果为109;第二次只设置第一个参数x的值为10,y使用默认值,返回结果为10。

调用方法时,如果参数较多,也可以按参数名设置数据,而不必完全按定义的顺序指定数据,如下面的代码。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Add(x: 10, z: 99);
            Add(y: 10, z: 99, x: 0);
        }
        //
        static void Add(int x, int y = 0, int z = 0)
        {
            Console.WriteLine("{0}+{1}+{2}={3}",
                x, y, z, x + y + z);
        }
    }
}

Add()方法的参数按x、y、z定义,在Main()方法中调用Add()方法进,使用“参数名:值”的格式指定参数数据,可以看到,在Add()方法中正确地显示了参数对应的值和计算结果。

使用参数名设置参数数据

参数数组应用于参数类型相同,但数量未定的情况,调用方法时可以设置0个或多个参数,如下面的代码。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Add());  // 0
            Console.WriteLine(Add(1));  // 1
            Console.WriteLine(Add(1,2,3));  // 6
        }
        //
        static int Add(params int[] args)
        {
            int sum = 0;
            for (int i = 0; i < args.Length; i++)
                sum += args[i];
            return sum;
        }
    }
}

参数数组在方法体中作为数组(Array)对象调用,本例的Add()方法中,使用for循环语句计算了所有参数的和并返回。Main()方法中调用了三次Add()方法,分别设置了0、1、3个参数。

重载(overload)是指具有相同名称的多个方法版本,但各个版本的参数设置不同,如下面的代码。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Add(10, 99));
            Console.WriteLine(Add(1.1, 99));  
        }
        //
        static int Add(int x,int y)
        {
            Console.WriteLine("int Add(int,int)");
            return x + y;
        }
        //
        static double Add(double x, double y)
        {
            Console.WriteLine("double Add(double,double)");
            return x + y;
        }
    }
}

代码中定义了两个Add()方法,第一个版本的参数和返回值都是int类型,第二个版本的参数和返回值都是double类型;代码执行结果如下图所示。

方法的重载

下面的代码演示了按引用传递参数。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 10, b = 99;
            Console.WriteLine("a={0}, b={1}", a, b);
            Swap(ref a, ref b);
            Console.WriteLine("a={0}, b={1}", a, b);
        }
        //
        static void Swap(ref int x,ref int y)
        {
            int tmp = x;
            x = y;
            y = tmp;
        }
    }
}

代码中定义了Swap()方法,其功能是交换两个参数的值,这里使用ref关键字将参数定义为按引用传递,只有这样,在方法中才能真正修改传入的变量数据,否则在方法中操作的只是传入变量的副本,无法真正修改外部变量的值。本例执行结果如下图所示。

按引用传递数据

需要注意的是,类类型默认是按引用传递,方法中可以修改实例的数据,但不能修改对象指向的实例,如果需要修改外部对象的实例,应在方法参数中使用ref关键字定义。结构等值类型则是按值传递,需要在方法中修改外部变量的值时必须使用ref关键字定义为按引用传递参数。

如果一个方法需要返回的数据不止一个,可以使用输出参数实现,方法定义中,输出参数使用out关键字定义,如下面的代码。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            object obj = "abc";
            int result;
            if (ToInt(obj, out result))
                Console.WriteLine("成功转换为整数{0}", result);
            else
                Console.WriteLine("无法转换为整数");
        }
        //
        static bool ToInt(object obj ,out int result)
        {
            result = 0;
            return obj != null && int.TryParse(obj.ToString(), out result);
        }
    }
}

代码中的ToInt()方法第二个参数使用输出参数,用于输出转换的整数结果,而方法本身会返回布尔类型(bool)类型,判断是否转换成功;方法中调用的int.TryParse()方法的第二个参数也定义为输出参数,这里直接使用ToInt()方法的result参数输出。

Main()方法中,首先定义obj对象保存需要转换的数据,这里可以修改obj对象的值来观察执行结果。变量result用于保存ToInt()方法输出的转换结果数据,如果obj对象的值可以转换为int类型,则显示其转换结果,否则显示"无法转换为整数"。

构造函数与初始化器

构造函数会在创建类的实例时调用,在类中使用与类同名的方法创建构造函数,如下面的代码(/cfx/demo/C1.cs)。

C#
using System;

namespace cfx.demo
{
    public class C1
    {
        public C1() { }
        public C1(string name)
        {
            Name = name;
        } 
        //
        public string Name = "";
        //
        public void SayHello()
        {
            Console.WriteLine("Hello,{0}.", Name);
        }
        //
        public uint Age { get; set; }
    }
}

代码中,在C1类中创建了两个构造函数,即两个重载版本的构造函数。需要注意,当类中没有定义构造函数时,会自动包含一个没有参数的构造函数;如果创建了有参数的构造函数,则不再默认包含无参数的构造函数,如果需要则必须创建一个。

下面的代码,在Program.cs文件中测试C1类的构造函数。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1();
            C1 obj2 = new C1("Tom");
            obj2.SayHello();
        }
    }
}

执行代码会显示“Hello,Tom.”。

此外,构造函数还可以调用其它的构造函数,并且,构造函数还可以对只读属性进行赋值,如下面的代码(/cfs/demo/C1.cs)。

C#
using System;

namespace cfx.demo
{
    public class C1
    {
        public C1():this("Noname") { }
        public C1(string name)
        {
            Name = name;
        } 
        //
        public string Name { get; private set; }
        //
        public void SayHello()
        {
            Console.WriteLine("Hello,{0}.", Name);
        }
    }
}

本例,将Name属性定义为只读属性,C1()构造函数调用了C1(string)构造函数,并指定默认姓名(Name属性)为“Noname”;C1(string)构造函数的参数需要指定Name属性值。下面的代码,在Program.cs文件中进行测试。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1();
            C1 obj2 = new C1("Tom");
            obj1.SayHello();  // Hello,Noname.
            obj2.SayHello();  // Hello,Tom.
        }
    }
}

执行代码会显示"Hello,Noname."和"Hello,Tom."。

实际中,还可以根据需要修改构造函数定义的修饰符,如使用private、protected、internal、static等关键字。

虽然构造函数可以通过重载、可选参数等创建多个版本,但是,如果属性过多,构造函数的定义也会显得麻烦,此时,属性可以设置为常用的默认值,然后,在创建对象时使用初始化器设置所需要的属性值,即在创建实例后使用一对花括号定义属性值,如下面的代码。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1()
            {
                Name="Tom"
            };
            //
            obj1.SayHello();
        }
    }
}

此外,设置多个属性数据时使用逗号分隔。

析构函数与using语句结构

析构函数可以在对象释放时自动调用,其命名格式为“~<类名>”,不需要修饰符和返回值类型,如下面的代码(/cfs/demo/C1.cs)。

C#
using System;

namespace cfx.demo
{
    public class C1
    {
        public C1():this("Noname") { }
        public C1(string name)
        {
            Name = name;
        } 
        //
        public string Name { get; private set; }
        //
        public void SayHello()
        {
            Console.WriteLine("Hello,{0}.", Name);
        }
        //
        ~C1()
        {
            Console.WriteLine("{0}已穿越……", Name);
        }
    }
}

下面的代码,在Program.cs文件进行测试。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1();
            C1 obj2 = new C1("Tom");
        }
    }
}

本例创建了两个C1类型的对象,然后什么也没做。对象不再使用时会自动释放内存,此时会自动调用C1类的析构函数,执行效果如下图所示。

析构函数

另一种能够自动调用对象清理的机制是使用using语句结构操作对象,此时的对象类型应实现了IDisposable接口,在对象释放时,无论代码是否正确执行,都会调用Dispose()方法,如下面的代码(/cfx/demo/C1.cs)。

C#
using System;

namespace cfx.demo
{
    public class C1 : IDisposable
    {
        public C1():this("Noname") { }
        public C1(string name)
        {
            Name = name;
        } 
        //
        public string Name { get; set; }
        //
        public void SayHello()
        {
            Console.WriteLine("Hello,{0}.", Name);
        }

        public void Dispose()
        {
            Console.WriteLine("对象已清理");
        }

        //
        ~C1()
        {
            Console.WriteLine("{0}已穿越……", Name);
        }
    }
}

下面的代码,在Program.cs文件中进行测试。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (C1 obj1 = new C1("Tom"))
            {
                obj1.SayHello();
            }
        }
    }
}

代码执行结果如下图所示。

using语句结构与IDisposable接口

使用 using语句结构可以保证无论结构中的代码是否正确执行,都可以在对象释放前对资源进行必要的清理,如关闭打开的文件、关闭打开的数据库连接等。

索引器

索引器可以让对象使用一对中括号按索引访问数据,如数组(Array)、列表(List)、字典(Dictionary)等对象。在自定义类中,可以使用“this[<索引类型> <索引变量>]”格式创建索引器,如下面的代码(/cfx/demo/)。

C#
using System.Collections.Generic;
// 斐波那契数列(Fibonacci Sequence)
namespace cfx.demo
{
    public class Fib
    {
        private List<long> mySeq;
        //
        public Fib()
        {
            mySeq = new List<long>();
            mySeq.Add(1);
            mySeq.Add(1);
        }
        // 读取数列的数据,n是大于等于0的整数
        public long this[int n]
        {
            get
            {
                // 转换为mySeq的索引
                if (n < mySeq.Count)
                {
                    return mySeq[n];
                }
                else
                {
                    // 根据读取需要生成数列中的数值
                    for (int i = mySeq.Count; i <= n; i++)
                        mySeq.Add(mySeq[i - 2] + mySeq[i - 1]);
                    //
                    return mySeq[n];
                }
            }
        }
    }
}

Fib类的功能是实现斐波那契数列(Fibonacci Sequence),其中,索引器使用大于等于0的值,即第一个读取的值索引是0,第二个索引值是1,以此类推。索引器中同样使用get块返回读取的数据,本例定义为只读索引器,如果定义索引器的写入操作,应使用set块,与属性的定义类似。

下面的代码,在Program.cs文件中显示斐波那契数列的前10个数值。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Fib fib = new Fib();
            for(int i =0; i < 10; i++)
            {
                Console.WriteLine(fib[i]);
            }
        }
    }
}

代码执行效果如下图所示。

显示斐波那契数列

定义索引器时,索引类型除了使用整数,还可以使用字符串等类型,可以根据数据结构的实际需要设置。

静态类、静态方法与扩展方法

前面创建的类成员需要使用类的实例(对象)调用,称为类的实例成员,每一个实例都会有独立的副本。工作中,有些代码只需要一个副本,此时可以定义为静态成员,如下面的代码(/cfx/demo/ObjectFactory.cs)。

C#
using System;

namespace cfx.demo
{
    public class ObjectFactory
    {
        // 创建对象计数
        public static int Counter;
        // 静态构造函数
        static ObjectFactory()
        {
            Counter = 0;
        }
        //
        public ObjectFactory()
        {
            Counter = Counter + 1;
            Console.WriteLine("创建第{0}个对象...", Counter);
        }
        //
    }
}

代码中创建了ObjectFactory类,其中,Counter为静态字段,用于保存创建对象的数量;然后定义了静态构造函数,其中将Counter字段的值设置为0。最后是实例化的构造函数,每一次创建实例时Counter字段都会加1,并显示创建第几个对象。下面的代码,在Program.cs文件中测试ObjectFactory类。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            ObjectFactory f1 = new ObjectFactory();
            ObjectFactory f2 = new ObjectFactory();
            ObjectFactory f3 = new ObjectFactory();
        }
    }
}

代码中,创建了三个ObjectFactory对象,执行结果如下图所示。

测试静态成员

类中的静态成员可以创建构造函数、字段、属性、方法等要素,这些静态成员由类名直接调用,并可以在实例成员中调用,但反过来,实例成员不能在静态成员中调用,因为实例成员只属于某一个具体的实例。

除了定义类的静态成员,类也可以定义为静态的。静态类一般用于封装代码,如System命名空间中的Console类、Math类等。静态类和静态方法还可以用于扩展现有类的实例方法,下面的代码(/cfx/Str.cs)演示了相关应用。

C#
using System;
using System.Text.RegularExpressions;

namespace cfx
{
    public static class Str
    {
        // 转换为int类型,默认值为0
        public static int ToInt(this string s, int defVal = 0)
        {
            int result;
            if (int.TryParse(s, out result))
                return result;
            else
                return defVal;
        }
    }
}

代码中定义了Str静态类,其中,定义了ToInt()静态方法,请注意,第一个参数定义为string类型,并使用了this关键字,此时,ToInt()方法就定义为string类型的扩展方法,可以通过字符串对象直接调用。ToInt()方法的第二个参数指定默认值,当参数一的字符串能够正确转换为int类型时返回转换结果,否则返回默认值。

下面的代码,在Program.cs文件中测试Str.ToInt()方法。

C#
using System;
using cfx;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "123";
            Console.WriteLine(s.ToInt());  // 123
            Console.WriteLine("abc".ToInt());  // 0
            Console.WriteLine(Str.ToInt("99"));  // 99
        }
    }
}

代码中,第一次使用字符串对象s调用ToInt()方法;第二次使用字符串字面量调用ToInt()方法;第三次使用类名和方法名调用Str.ToInt()方法。

空值处理

当对象没有赋予类的实例时,其值为空值,使用null关键字表示。代码中,结构类型,如各种数值类型是不能为空值的,但可以在标准类型关键字后添加问号(?)创建对应的可空类型,如下面的代码。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            int? x = null;
            if (x == null)
                Console.WriteLine("变量为空");
            else
                Console.WriteLine("x={0}", x);
        }
    }
}

执行代码会显示“变量为空”,可以修改变量x的值来观察运行结果。

??运算符需要两个运算数,左侧为对象,当对象不是空值(null)时返回对象,否则返回运算符右侧表达式的值,如下面的代码。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1();
            C1 obj2 = obj1 ?? new C1("Tom");
            obj2.SayHello();
        }
    }
}

执行代码会显示“Hello.Noname.”,如果将obj1设置为null,则会显示“Hello,Tom.”。

?运算符可以放在对象右侧,当对象不为空值时调用成员,否则不执行任何操作,如下面的代码。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1 obj1 = new C1();
            obj1?.SayHello();
        }
    }
}

执行代码会显示“Hello,Noname.”,如果将obj1设置为null,则什么也不执行。

var关键字与匿名类型

使用var关键字可以在声明变量或对象时不指定类型,变量或对象类型会根据赋值的数据而定;此外,对于一次性使用的数据结构,可以直接使用匿名类型定义,下面的代码演示了var关键字和匿名类型的应用。

C#
using System;
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            var obj1 = new C1();
            obj1?.SayHello();
            //
            var obj2 = new { Name = "Tom", Age = 25 };
            Console.WriteLine("Name:{0}, Age:{1}", obj2.Name, obj2.Age);
        }
    }
}

执行结果如下图所示。

var与匿名类型

Type类与类型处理

代码中,可以通过Type类获取变量、对象或字面量的类型信息。Type类为抽象类,不能直接实例化,但可以通过变量、对象和字面量的GetType()方法获取类型对象,也可以使用typeof运算符通过类型名称获取Type对象,然后读取相关的信息,如下面的代码。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            int x = 123;
            Type t1 = x.GetType();
            Type t2 = typeof(string);
            //
            Console.WriteLine(t1.Name);  // Int32
            Console.WriteLine(t2.Name);  // String
        }
    }
}

代码中,获取了整数(int)和string类型的Type对象,并通过Name属性显示类型名称。处理类型时应注意,Type对象中显示的名称是.NET Framework中定义的类型名称。

Type对象中常用的属性有:

  • Name,类型在.NET Framework中的名称。
  • FullName,包括命名空间和类型名称。
  • Namespace,类型定义的命名空间。
  • IsValueType,是否为值类型,如结构、枚举类型。
  • IsClass,是否为引用类型,如类、委托类型。
  • BaseType,类型的直接基类类型。System.Object是唯一没有基类的类型,此属性返回null值。
  • IsAbstract,类型是否为抽象类。
  • IsArray,是否为数组类型。
  • IsGenericType,是否为泛型类型。
  • IsInterface,是否为接口类型。
  • IsNested,是否为嵌套类型,即定义在其它类型中的类型,如在类中还可以定义类、枚举等类型。
  • IsEnum,是否为枚举类型。

Type对象中常用的方法有:

  • IsSubclassOf(type),判断类型是否为type类型的子类,type类型同样为Type对象。
  • IsEnumDefined(obj),判断obj是否为当前枚举类型中的成员。
  • GetMethod(<方法名>),获取指定方法的信息,返回类型为System.Reflection.MethodInfo。如果指定的方法不存在则返回null;GetMethod()方法可以判断对象中是否可以调用指定的方法。
  • GetProperty(<属性名>),获取指定属性的信息,返回类型为System.Reflection.PropertyInfo;属性不存在时返回null。
  • GetField(<字段名>),返回指定的属性信息,返回类型为System.Reflection.FieldInfo;字段不存在时返回null。
  • GetEvent(<事件名>)方法,返回指定的事件信息,返回类型为System.Reflection.EventInfo;事件不存在时返回null。
  • GetInterfaces(),返回当前类型实现的接口,返回类型为Type[],即Type对象数组。

关于接口、继承、枚举、委托与事件、泛型等内容会在后续文章中详细讨论。

Lambda表达式

Lambda表达式是一种简化方法定义的语法格式,可以定义匿名方法。定义Lambda表达式使用=>运算符,运算符左侧可以看作是方法的参数,右侧是方法的实现代码,右侧表达式的值即是方法的返回值。下面的代码演示了Lambada表达式的应用。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            int x = 10, y = 99;
            Console.WriteLine(Add(x, y));
        }
        //
        static int Add(int x, int y) => x + y;
    }
}

代码中,Add()方法的定义使用了Lambda表达式,省略了花括号定义的方法体;这里定义的Add()方法与下面的代码功能相同。

C#
static int Add(int x, int y) 
{ 
    return x + y;
}

结构

结构使用struct关键字定义,其中,可以定义构造函数、变量、常量、方法、属性、索引器、事件、重载运算符等成员。定义结构的语法与类相似,如下面的代码。

C#
using System;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Point pt = new Point(10, 99);
            Console.WriteLine("({0}, {1})", pt.X, pt.Y);
        }
    }
    //
    struct Point
    {
        public int X;
        public int Y;
        //
        public Point(int x,int y)
        {
            X = x;
            Y = y;
        }
    }
}

代码中定义了Point结构,其中包括两个字段X和Y;此外,还定义了构造函数,其参数包括x和y。Main()方法中,首先创建Point结构类型变量pt,并指定字段数据分别为10和99,最后显示信息为“(10, 99)”。

实际应用中,类为引用类型,结构为值类型。此外,类还可以通过实现接口类型和继承等方式定义更加复杂的代码结构,后续文章中还会有详细讨论。