小新的技术天地

Make It Works !

博客园 首页 新随笔 联系 订阅 管理

原作者:Nishant S

Introduction

Just about everything is a heap object when you are using C#. Only elementary native types like int are treated as value types. But there are two value types in C# that are pretty much more useful that first glances would tell you. They are the enum and struct types. Very few tutorials even cover these topics, but they have their own uses. And both of them are a lot more efficient than classes and you can use them in place of classes when they meet your requirements to improve performance.

介绍

 

当你使用C#时,几乎所有东西都是储存在堆上的对象。只有基本的原生类型比如说 int 作为值类型创建。但有两种值类型当你第一眼看到时就会觉得更加有用。它们是enum struct类型。几乎没有指导书有这两个类型的主题,但它们自有用处。而且它们两个都有比类更加有效率的地方。当你有需要改善你的程序的时候,你可以用它们两个来代替类。

 

Enums

Enums are basically a set of named constants. They are declared in C# using the enum keyword. Every enum type automatically derives from System.Enum and thus we can use System.Enum methods on our Enums. Enums are value types and are created on the stack and not on the heap. You don't have to use new to create an enum type. Declaring an enum is a little like setting the members of an array as shown below.

enum Rating {Poor, Average, Okay, Good, Excellent}

 

枚举

 

Enums(枚举)基本上是一些取了名的常量的集合。它们在C# 中用 enum 关键字声明。所有的枚举类型都自动(隐含)得从System.Enum继承,所以我们可以在我们自己的枚举类型中使用System.Enum中的方法。枚举是值类型,它被创建在堆栈上而不是堆上。你不必使用new操作符来创建一个枚举类型。声明一个枚举有点像为一个数组设置数组成员,如下所示:

enum Rating {Poor, Average, Okay, Good, Excellent}

 

 

You can pass enums to member functions just as if they were normal objects. And you can perform arithmetic on enums too. For example we can write two functions, one to increment our  enum and the other to decrement our enum.

 

你可以把枚举传递给成员函数就好像它们是普通的对象。你也可以在枚举上进行算术运算。比如说我们写了两个函数,一个用来使我们的枚举自增(increment),而另一个用来使它自减(decrement)

Rating IncrementRating(Rating r)
{
    
if(r == Rating.Excellent)
        
return r;
    
else
        
return r+1;
}

Rating DecrementRating(Rating r)
{
    
if(r == Rating.Poor)
        
return r;
    
else
        
return r-1;
}



Both functions take a Rating object as argument and return back a Rating object. Now we can simply call these functions from elsewhere.

两个函数都把Rating对象作为参数,同时返回一个Rating对象值。现在我们就可以从任何地方简单地调用这些函数了

for (Rating r1 = Rating.Poor; 
    r1 
< Rating.Excellent ; 
    r1 
= IncrementRating(r1))
{           
    Console.WriteLine(r1);
}


Console.WriteLine();

for (Rating r2 = Rating.Excellent; 
    r2 
> Rating.Poor; 
    r2 
= DecrementRating(r2))
{
    Console.WriteLine(r2);          
}


And here is a sample code snippet showing how you can call System.Enum methods on our Enum object. We call the GetNames method which retrieves an array of the names of the constants in the enumeration.

这是一个示例代码小片段,用来展示我们如何调在我们的Enum对象里用System.Enum中的方法。我们可以使用GetNames方法来得到一个数组,这个数组包含定义在枚举中的常量成员。

foreach(string s in Rating.GetNames(typeof(Rating)))
    Console.WriteLine(s);

Where to use enums

Quite often we have situations where a class method takes as an argument a custom option. Let's say we have some kind of file access class and there is a file open method that has a parameter that might be one of read-mode, write-mode, read-write-mode, create-mode and append-mode. Now you might think of adding five static member fields to your class for these modes. Wrong approach! Declare and use an enumeration which is a whole lot more efficient and is better programming practice in my opinion.

哪里该使用枚举

我们经常遇到这种情况,一个类的方法把参数作为自选项。比如说我们有某个文件存取类,其中有一个打开文件的方法,它有一个参数,可能是只读模式,只写模式,读写模式,创建模式,追加模式中的一种。这时你可能想为这些模式加5个静态字段作为类成员。错了!在我看来,声明和使用枚举效率高得多的了,是一个更好的编程习惯。

PS:我把argumentparameter都翻译成参数,但是不知道它们的区别,希望高手指点)
    (经过Ninputer指出,应该是argument是“实参” ,parameter是“形参” )

Structs

In C++ a struct is just about the same as a class for all purposes except in the default access modifier for methods. In C# a struct are a pale puny version of a class. I am not sure why this was done so, but perhaps they decided to have a clear distinction between structs and classes. Here are some of the drastic areas where classes and structs differ in functionality.

  • structs are stack objects and however much you try you cannot create them on the heap
  • structs cannot inherit from other structs though they can derive from interfaces
  • You cannot declare a default constructor for a struct, your constructors must have parameters
  • The constructor is called only if you create your struct using new, if you simply declare the struct just as in  declaring a native type like int, you must explicitly set each member's value before you can use the struct

结构

C++里,除方法的默认访问修饰符,任何情况结构都跟类是一样的。(我没学过C++,就不知道这个是什么意思了)。在C#里,结构可以算是一种功能较弱的类。我不确定为什么会被这样处理,可能他们决定让结构跟类有一个明确的区别。下面是一些类跟结构在功能上明显不同的地方:

  • 无论你多想把结构创建在堆上,结构都是存放在堆栈上的对象
  • 一个结构无法从另一个结构继承,但它们可以继承接口(实现接口)
  • 你不能为结构声明一个默认构造器 ,你的构造器必须有参数
  • 只有在你创建结构的时候使用new操作符,构造器才会被调用。如果你只是简单的声明结构就像声明原生类型int那样,你就必须在使用这个结构以前,显式地为每个结构的成员赋值。

 

struct Student : IGrade
{   
    
public int maths;
    
public int english;
    
public int csharp;

    
//public member function
    public int GetTot()
    
{
        
return maths+english+csharp;
    }


    
//We have a constructor that takes an int as argument
    public Student(int y)
    
{
        maths 
= english = csharp = y;
    }


    
//This method is implemented because we derive
    
//from the IGrade interface
    public string GetGrade()
    
{
        
if(GetTot() > 240 )
            
return "Brilliant";
        
if(GetTot() > 140 )
            
return "Passed";
        
return "Failed";
    }

}


interface IGrade
{
    
string GetGrade();
}

 

Well, now let's take a look at how we can use our struct.

好了,现在让我们来看看怎么使用我们的结构。

Student s1 = new Student();
Console.WriteLine(s1.GetTot());
Console.WriteLine(s1.GetGrade());
 
//Output
0
Failed

Here the default constructor gets called. This is automatically implemented for us and we cannot have our own default parameter-less constructor. The default parameter-less constructor simply initializes all values to their zero-equivalents. This is why we get a 0 as the total.

这里默认构造器被调用了。这是自动执行的,我们不能有自己的默认无参构造器(default parameter-less constructor我不知道这样翻译对不对,但我觉得应该是这个意思)。默认无参构造器只是简单地把所有值初始化为它们的0等价值(zero-equivalents,虽然翻译的不伦不类的,但我想大家应该知道是什么意思,哈),这就是为什么我们的total变量得到了0值。

Student s2;
s2.maths = s2.english = s2.csharp = 50;
Console.WriteLine(s2.GetTot());
Console.WriteLine(s2.GetGrade());
 
//Output
150
Passed

Because we haven't used new, the constructor does not get called. Of all the silly features this one must win the annual contest by a long way. I see no sane reason why this must be so. Anyway you have to initialize all the member fields. If you comment out the line that does the initialization you will get a compiler error :- Use of unassigned local variable 's2'

因为我们没有使用new操作符,构造器就没有被调用。在所有愚蠢的特性中,这一个定会在年度讨论会中压倒性胜出。我没有发现任何健全的理由为什么这样做。总之你必须自己初始化所有成员字段。如果你把那行(初始化所有成员字段的那行)注释掉,你会得到一个编译器错误:

error CS0165: 使用了未赋值的局部变量“s2(这是我的编译器显示的,哈)

Student s3 = new Student(90);
Console.WriteLine(s3.GetTot());
Console.WriteLine(s3.GetGrade());
 
//Output
270
Brilliant

This time we use our custom constructor that takes an int as argument.

这次我们的自定义构造器把一个int作为参数了。

When to use structs

Because structs are value types they would be easier to handle and more efficient that classes. When you find that you are using a class mostly for storing a set of values, you must replace those classes with structs. When you declare arrays of structs because they are created on the heap, efficiency again improves. Because if they were classes each class object would need to have memory allocated on the heap and their references would be stored. In fact lots of classes within the .NET framework are actually structs. For example System.Drawing.Point is actually a struct and not a class.

什么时候该使用结构

因为结构是值类型,它们比类更加易于操作,更加有效率。当你发现你正在使用一个类主要用于储存一些值的集合,你就必须用结构代替类。当你声明一个结构数组,由于它们被创建在堆上,效率又一次被提高了。如果它们是类,则每一个类的对象都要在堆上分配空间,它们的对象的引用就被储存了(我觉得引用应该是在堆栈上)。事实上很多.NET框架里的类实际上是结构。比如说System.Drawing.Point实际上是一个结构而不是一个类。

 原文及源代码请参见
http://www.codeproject.com/csharp/csenums01.asp

posted on 2004-10-15 17:10  小新0574  阅读(4998)  评论(11编辑  收藏  举报