C# --索引器四个例子

C# 索引器四个例子:普通索引器,字符串索引器,接口定义索引器,泛型索引器。

//普通索引器

using System;

namespace Index1
{
    class TempRecord
    {
        private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,
                                            61.3F, 65.9F, 62.1F, 59.2F, 57.5F };
        System.DateTime date { get; set; }
        public int Length
        {
            get { return temps.Length; }
        }
        public float this[int index]
        {
            get
            {
                return temps[index];
            }

            set
            {
                temps[index] = value;
            }
        }
    }

    class MainClass
    {
        static void Main()
        {
            TempRecord tempRecord = new TempRecord();
            tempRecord[3] = 58.3F;
            tempRecord[5] = 60.1F;
            for (int i = 0; i < 10; i++)
            {

                if (i < tempRecord.Length)
                {
                    System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
                }
                else
                {
                    System.Console.WriteLine("Index value of {0} is out of range", i);
                }
            }
        }
    }
}

效果:


//字符串,自定义索引器

using System;

namespace Index2
{
    class DayCollection
    {
        string[] days = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日" };
        private int GetDay(string testDay)
        {
            int i = 0;
            foreach (string day in days)
            {
                if (day == testDay)
                {
                    return i;
                }
                i++;
            }
            return -1;
        }
        public int this[string day]
        {
            get
            {
                return (GetDay(day));
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            DayCollection week = new DayCollection();
            System.Console.WriteLine(week["星期五"]);
            System.Console.WriteLine(week["星期八"]);
        }
    }
}

效果:


//泛型索引器

using System;


namespace Index3
{
    class SampleCollection<T>
    {
        private T[] arr = new T[100];
        //this 关键字用于定义索引器。
        //索引器不必根据整数值进行索引,自定义决定如何定义特定的查找机制。
        //索引器可以有多个形参,例如当访问二维数组时。
        //索引器可被重载。
        public T this[int i]
        {
            get
            {
                return arr[i];
            }
            set
            {
                arr[i] = value;
            }
        }
    }

    // This class shows how client code uses the indexer
    class Program
    {
        static void Main(string[] args)
        {
            SampleCollection<string> stringCollection = new SampleCollection<string>();
            stringCollection[0] = "Hello, World";
            System.Console.WriteLine(stringCollection[0]);
        }
    }
}

效果:


//接口实现索引器

using System;

namespace Index4
{
    public interface ISomeInterface
    {
        int this[int index]
        {
            get;
            set;
        }
    }
    class IndexerClass : ISomeInterface
    {
        private int[] arr = new int[100];
        public int this[int index]
        {
            get
            {
                if (index < 0 || index >= 100)
                {
                    return 0;
                }
                else
                {
                    return arr[index];
                }
            }
            set
            {
                if (!(index < 0 || index >= 100))
                {
                    arr[index] = value;
                }
            }
        }
    }
    class MainClass
    {
        static void Main()
        {
            IndexerClass test = new IndexerClass();
            test[2] = 4;
            test[5] = 32;
            for (int i = 0; i <= 10; i++)
            {
                System.Console.WriteLine("Element #{0} = {1}", i, test[i]);
            }
        }
    }
}

效果:



posted @ 2012-04-11 20:44 青蛙學堂 阅读(1688) | 评论 (0)编辑 收藏

c#--资源释放--析构


有些对象,如connection、DataReader、DataSet等等,最后用完了后是用close()合适还是用dispose()?或者两个都要加上?还是最后都只用dispose()就可以?
我想知道的是怎么样才最少占用资源、程序性能最高?希望给出有力的答案,最好有参考,谢谢

DataReader,DataSet在使用完后,Close.
connection一般是写在using里的,例如:
using(SqlConnection sqlConn=......)
{
//连接数据库
//对数据库操作,其间可能会用到DataReader,DataSet
//....
//关闭DataReader,DataSet
sqlConn.Close();
}

这样,sqlConn会在花括号结束时被自动Dispose,及时回收.

以connection来说,close是关闭连接,但是它是链接对象仍然存在,下一次需要该链接,只需要调整用open方法就行,而dispose()则是析构链接对象,当你需要链接时,需要重新实例化connection对象,和open()链接。
一般使用会用close(),
dispose()会释放内存,我一般不用这个,因为C#的垃圾回收就自动帮我们搞定,函数的作用域完成,就会自动析构,不用我们显式地析构,除非你的程序对内存空间有苛刻的要求 





posted @ 2012-04-11 20:16 青蛙學堂 阅读(1014) | 评论 (2)编辑 收藏

c#--重写--重载--多态


一:重写是指重写基类的方法,在基类中的方法必须有修饰符virtual ,而在子类的方法中
必须指明override.
格式:
基类中:
public virtual void myMethod()
{}
子类中:
public override void myMethod()
{}
重写后,基类和子类对象访问myMethod()方法,结果都是子类重新定义后的方法,基类的方法相当
于被覆盖掉了。

二:重载
用于在给定了参数列表和一组候选函数成员的情况下,选择一个最佳成员来实施调用。

public void test(int x,int y ) {}

public void test(int x,ref y ) {}

public void test(int x,int y,string a ) {}

注:可考虑用泛型优化此用途。


三:多态性概述
当派生类从基类继承时,它会获得基类的所有方法、字段、属性和事件。若要更改基类的数据和行为,您有两种选择:可以使用新的派生成员替换基成员,或者可以重写虚拟的基成员。

使用新的派生成员替换基类的成员需要使用 new 关键字。如果基类定义了一个方法、字段或属性,则 new 关键字用于在派生类中创建该方法、字段或属性的新定义。new 关键字放置在要替换的类成员的返回类型之前。例如:

 

public class BaseClass
{
    public void DoWork() { }
    public int WorkField;
    public int WorkProperty
    {
        get { return 0; }
    }
}

public class DerivedClass : BaseClass
{
    public new void DoWork() { }
    public new int WorkField;
    public new int WorkProperty
    {
        get { return 0; }
    }
}

使用 new 关键字时,调用的是新的类成员而不是已被替换的基类成员。这些基类成员称为隐藏成员。如果将派生类的实例强制转换为基类的实例,就仍然可以调用隐藏类成员。例如:


 
DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

 

 

 


 

posted @ 2012-04-11 16:58 青蛙學堂 阅读(240) | 评论 (0)编辑 收藏

c#--重载-重写-覆盖

overload:重载指的是同一个类中有两个或多个名字相同但是参数不同的方法,(注:返回值不能区别函数是否重载),重载没有关键字
override:过载也称重写是指子类对父类中虚函数或抽象函数的“覆盖”(这也就是有些书将过载翻译为覆盖的原因),但是这种“覆盖”和用new关键字来覆盖是有区别的。
new:覆盖指的是不同类中(基类或派生类)有两个或多个返回类型、方法名、参数都相同,但是方法体不同的方法。
但是这种覆盖是一种表面上的覆盖,所以也叫隐藏,被覆盖的父类方法是可以调用得到的。
重载覆盖的发生条件:
重载,必然发生在一个类中,函数名相同,参数类型或者顺序不同构成重载,与返回类型无关
重写,必然发生在基类和派生类中,其类函数用virtual修饰,派生类用override修饰
覆盖,在子类中写一个和基类一样名字(参数不同也算)的非虚函数,会让基类中的函数被隐藏,编译后会提示要求使用New关键字
重载示例:

        public void Fun()
{
Console.WriteLine(
"I am F");
}
public void Fun(int i)
{
Console.WriteLine(
"I am F,i={0}",i);
}

override重写特性:
override 声明重写的方法称为重写基方法,重写的基方法必须与 override 方法具有相同的签名。
重写的基方法必须是 virtualabstractoverride
,不能重写非虚方法或静态方法
override的方法和virtual的方法必须具有相同的访问级别修饰符
,不能更改 virtual 方法的可访问性
不能使用newstaticvirtual 修饰符来修改 override 方法。

重写属性声明必须指定与继承属性完全相同的访问修饰符、类型和名称,并且被重写的属性必须是virtualabstractoverride 的。
覆盖示例:
当我们没有使用覆盖时,派生类继承基类,结果如下:

    class A
{
public void Fun()
{
Console.WriteLine(
"I am F");
}
}
class Program:A
{

static void Main(string[] args)
{
Program p
= new Program();
p.Fun();
Console.Read();
}
}
//结果为:I am F

当我们覆盖原来的方法呢?

    class A
{
public void Fun()
{
Console.WriteLine(
"I am F");
}
}
class Program:A
{
public new void Fun()
{
int i = 1;
Console.WriteLine(
"I am F,i={0}", i);
}
static void Main(string[] args)
{
Program p
= new Program();
p.Fun();
Console.Read();
}
}
//结果为:I am F,i=1

new覆盖与重写、重载的区别:
当子类与父类的参数不同时
当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载)
当基类函数是虚函数时,基类函数将被隐藏。(
因为子类和基类不在同一范围内,所以不是重载;因为参数不同,所以不是重写
当子类与父类的参数相同时
当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载,因为基类不是虚函数,所以是隐藏不是重写)
当基类函数是虚函数时,基类函数将被覆盖。(因为子类和基类不在同一范围内,所以不是重载)
那么为什么不是重写呢?我们可以做一个例子还测试一下,这个例子在虚函数时已经举过,在这里为了说明此问题在重复一下:

    class A
{
public virtual void Fun()
{
Console.WriteLine(
"I am F");
}
}
class Program:A
{
public override void Fun()
{
int i = 1;
Console.WriteLine(
"I am F,i={0}", i);
}
static void Main(string[] args)
{
A p
= new Program();
p.Fun();
Console.Read();
}
}

我们知道,以上例子中,派生类存在一个对基类的重写方法,所以结果为:I am F ,i=1
若是我们把override换成new,那么如果是重写的话,会和上面的结果相等,但实际结果是什么呢?
实际的结果是:I am F
由此我们知道,当基类函数是虚函数时,基类函数不是重写,而是覆盖了基函数的同名函数。

posted @ 2012-04-11 16:34 青蛙學堂 阅读(456) | 评论 (1)编辑 收藏

c#--多态--解释

 

多态又称后期绑定,是一种在运行时(just in time)指定方法调用地址的技术。

通常,编译器在编译期就能知道方法的地址,运行时直接加载这个地址上的堆栈代码(.net中指中间代码)就可以了,这被称为静态绑定或前期绑定,虽然很少使用这个术语。

编译器在编译期不能确定方法的地址,而只能在运行时确定的就被称为后期绑定(或动态联编)。例如,对于object类的ToString方法,其方法定义是返回类的名称,但是,调用这个方法却不一定能够返回类的名称(如String类),因为它有可能被派生类重写,多态技术确保运行时能够调用到正确的方法。

关于多态和继承的区别,继承实现了类型重用,而多态实现了方法重用。

posted @ 2012-04-11 16:31 青蛙學堂 阅读(304) | 评论 (0)编辑 收藏

c#--封装的概念

对于一个具有丰富结构化程序设计经验的程序员来说,面向对象的程序设计可能会给他们带来非常不自然的感觉。封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。在本文中,我们将详细学习属性的特性。

封装的概念

可以把程序按某种规则分成很多“块“,块与块之间可能会有联系,每个块都有一个可变部分和一个稳定的部

分。我们需要把可变的部分和稳定的部分分离出来,将稳定的部分暴露给其他块,而将可变的部分隐藏起来,以

便于随时可以让它修改。这项工作就是封装.

例如:在用类实现某个逻辑的时候,类就是以上所说的块,实现功能的具体代码就是可变的部分,而public的

法或者属性则是稳定的部分。

封装的意义

封装的意义在于保护或者防止代码(数据)被我们无意中破坏。在面向对象程序设计中数据被看作是一个中心的原素并且和使用它的函数结合的很密切,从而保护它不被其它的函数意外的修改。

封装提供了一个有效的途径来保护数据不被意外的破坏。相比我们将数据(用域来实现)在程序中定义为公用的(public)我们将它们(fields)定义为私有的(privat)在很多方面会更好。

私有的数据可以用两种方式来间接的控制。下面我们看一些c#例子来学习这两种方法用以封装数据。第一种方法,我们使用传统的存、取方法第二种方法我们用属性(property)。无论我们使用哪种的方法,我们的目标是在使用数据的同时不能使它受到任何的破坏和改变。有如下好处

其一: 使用者只需要了解如何通过类的接口使用类,而不用关心类的内部数据结构和数据组织方法。

其二:高内聚,低耦合一直是我们所追求的,用好封装恰恰可以减少耦合

其三:只要对外接口不改变,可以任意修改内部实现,这个可以很好的应对变化

其四:类具有了简洁清晰的对外接口,降低了使用者的学习过程

用传统的读、写方法封装

让我们来看一个例子有一个类Department,为了操纵这个类中的数据(string departname)我们定义了一个读方法和一个写方法。
using system;
public class Department
{
private string departname;
.......

// 读方法
public string GetDepartname()
{
return departname;
}

//写方法
public void SetDepartname( string a)
{
departname=a;
}

}
通过上面的方法,我们可以保护私有数据不被外部程序所破坏。现在我们使用两个不同的方法来写和读数据
public static int Main(string[] args)
{
Department d = new Department();
d.SetDepartname("ELECTRONICS");
Console.WriteLine("The Department is :"+d.GetDepartname());
return 0;
}
在上面的例子中,我们不能直接访问类Department的实例d中的私有数据(string departname),我们只能通过这两个方法来访问。

用属性来实现封装

属性是c#引入的一种语言成分,只有很少的语言支持属性。通过对属性的读和写来保护类中的域。第一种方法体身也是一种好的方式,但用属性来实现封装会更方便。
现在我们来看一个例子:
using system;
public class Department
{
private string departname;
public string Departname
{
get
{
return departname;
}
set
{
departname=value;
}
}
}
public class Departmentmain
{
public static int Main(string[] args)
{
Department d= new Department();
d.departname="Communication";
Console.WriteLine("The Department is :{0}",d.Departname);
return 0;
}
}
通过上面的例子,我们可以看到如何通过属性来实现封装。属性具有两种操作get和set。Get用来返回属性域的值。Set通过value这个变量来给属性域赋值。属性可以设为只读的(read-only)。这只需属性只具有一个set操作。

只读属性

using system;
public class ReadDepartment
{
private string departname;
public ReadDepartment(string avalue)
{
departname=avalue;
}
public string Departname
{
get
{
return departname;
}
}
}
public class ReadDepartmain
{
public static int Main(string[] args)
{
ReadDepartment d= new ReadDepartment("COMPUTERSCIENCE");
Console.WriteLine("The Department is: {0}",d.Departname);
return 0;
}
}
在上面的例子中我们看到了如何来实现一个只读的属性。类ReadDepartment拥有一个Departname属性只实现了get操作。它省略了写操作。这个特别的类拥有一个构造器,用来接受一个字符串变量。类ReadDepartmain中的Main方法创建了一个新的对象d。对像d的实例使用了类ReadDepartment带有一个字符串参数的构造器。因为上面的属性是只读的,所以我们不给给域departname赋值并且我们只侧读取此域中的值。当然属性也可以是只写的(write-only),这只需属性只具有一个get操作。

只写属性

using system;
public class WriteDepartment
{
private string departname;

public string Departname
{
set
{
departname=value;
Console.WriteLine("The Department is :{0}",departname);
}
}
}
public class WriteDepartmain
{
public static int Main(string[] args)
{
WriteDepartment d= new WriteDepartment();
d.departname="COMPUTERSCIENCE";
return 0;
}
}
在上面的例子中我们看到了如何来实现一个只写的属性。类WriteDepartment拥有一个Departname属性只实现了set操作。它省略了读操作。

总结

封装是朝着面向对象程序设计迈出的第一步。本文向您展示了一些封装的知识。用传统的读、写两种方法可以实现封装,另一种实现封装的方法是使用属性。使用属性的好处在于对象的使用者可以用一条语句来操作内部的数据。


posted @ 2012-04-11 15:05 青蛙學堂 阅读(5357) | 评论 (0)编辑 收藏

c#--封装总结

面向对象思想有三个核心要素:封装、继承与多态。如能正确理解这三要素,那么基本上可以算是在编程中建立了面向对象思想。在第二节中我曾介绍,在 C#中,所有数据类型的实例都是“对象”,不过最能体现对象特质的类型,还是“类”,同时它也是C#中最重要、最频繁使用的类型。接下来,我将通过介绍 C#的类,来充分理解对象封装的概念。

所谓“对象”,形象地说,我们可以把它理解为一块积木。设计积木的人需要设计积木的外观与形状,还有内部的材质。堆积木的人对于内部的材质并不关心,他们只需要根据不同的外观与形状来决定堆放的位置。因此,对于开发者而言,要设计面向对象的程序,同时会是两个迥然不同的身份:设计者与使用者。

先谈谈使用者。使用者的身份,就是利用已经提供给你的所有对象,根据需求,设计出自己需要实现的程序。就如堆积木的过程。这恰恰是面向对象编程的优势所在,那就是“对象的重用”。已经设计好的对象,可以被不同的使用者调用,这些功能既然已经实现,对于使用者而言,当然就免去了自己去设计的过程。正如堆积木那样,既然有了现成设计好的积木,使用者所要做的工作就是把这些积木最后组合起来,堆成不同的形状。.Net Framework所提供的类库,就是这样的积木。

例如我们想把一个int类型转换成字符型,就没有必要自己去实现这种转换,直接调用.Net Framework提供的功能就可以了:
int i = 10;
string s = i.ToString();

再比如我们想弹出一个Windows消息框,同样可以直接使用.Net Framework现有的类库:
MessageBox.Show(“Message”);

在上述的例子中,i和MessageBox都是一个对象。

再谈谈设计者的身份。虽然.Net Framework的类库功能已经非常强大,但它不可能考虑到业务的方方面面,如果需要使用一个根本就不存在的对象,此时就需要自己来设计了。例如图书管理系统,可能就需要用户,图书等对象。这就需要开发者自己来设计这些对象。

既然最能体现“对象”思想的类型是“类”,我就来介绍一下C#中的类类型。C#中类的关键字是class。在一个class对象中,主要分为 field(字段)、property(属性)和method(方法),前面两个对应的是对象的属性,而method则对应对象的行为。一个典型的 class定义如下所示:
public class User
{
 private string m_name;
 private string m_password;
 private int m_tryCounter;
 public string Name
 {
  get {return m_name;}
  set {m_name = value;}
 }
 public string Password
 {
  get {return m_password;}
  set {m_password = value;}
 }
 public void SignIn()
 { 
  if (m_tryCounter < 3)
  {
   if (IsValid())
   { 
    m_tryCounter = 0;
    Console.WriteLine("User {0} was signed in.", m_name);
   }
   else
   {
    m_tryCounter++;
    Console.WriteLine("User {0} is invalid. Can’t Sign in.", m_name);
   }
  }
  else
  {
   Console.WriteLine("You try to sign in more than 3 times. You are be denied.");
  }
 }
 public void SignOut()
 {
  m_tryCounter = 0;
  Console.WriteLine("User {0} was signed out.", m_name);
 }
 private bool IsValid()
 {
  if (m_name.ToUpper() == "ADMIN" && m_password == "admin")
  {
   return true;
  }
  else
  {
   return false;
  }
 }
}

字符串m_name,m_password,m_tryCounter就是类User的字段,Name,Password是类User的属性,而SignIn、SignOut和IsValid则是类User的方法。

关于field,property和method,我会在之后的文章中介绍,这里主要介绍的是在这个类中出现的修饰符public、private等相关的知识。

前面说到对象好比是一个积木,设计者需要定义好这个积木的外观和形状,也要考虑积木内部的制作,例如选用的材质,以及是空心还是实心。如果将这个积木剖开来看,实际上该对象应分为内、外两层。由于使用者只关心外部的实现,因此设计者就需要考虑,哪些实现应暴露在外,哪些实现应隐藏于内。这就体现了对象的封装的思想。

封装对象,并非是将整个对象完全包裹起来,而是根据具体的需要,设置使用者访问的权限。在C#中,分别用修饰符public,internal,protected,private设定,分别修饰类的字段、属性和方法,甚至于类对象本身:
public:表明所有对象都可以访问;
protected internal:表明同一个程序集内的对象,或者该类对象以及其子类可以访问;
internal:表明只有同一个程序集的对象可以访问;
protected:表明只有该类对象及其子类对象可以访问;(关于继承,会在以后介绍)
private:表明只有对象本身在对象内部可以访问;

可以看出,public的开放性最大,其次是protected internal,private的开放性最小。internal和protected居中。那么,internal和protected哪一个开放范围更大呢?我认为,没有完全绝对的结论。它们的范围前者体现一个横向的概念,后者则体现纵向的概念。如果是internal,那么外部程序集对象自然不能访问,但只要是居于同一个程序集中,则所有对象都可以访问它;如果是protected,那么即使是外部程序集对象,只要它继承了该对象,就可以访问,而即使是同一程序集,如果对象不是该类对象的子类,也是无法访问的。打一个比方,在我们的传统文化中,是非常强调“宗族”观念的,一个宗族的族长,对于本族人而言,权力极大,甚至掌握了生杀大权。以一个州府的范围为例,internal就好比是知府大人,只要是该州府的百姓,都属于他的管辖范围,而不管他是哪一个宗族。protected则好比是宗族的族长,只要是这个宗族的成员,都要服从他,哪怕该成员属于其他州府。我以前看过《雍正王朝》,其中就有这样一个情节,身为皇子的胤祯,竟然无法挽救自己心爱女人的命运,因为这个女人违反了她们宗族的族规,最后眼看着她被活活烧死,却只能黯然神伤,梦里萦回。

以前面定义的User类而言,所有的字段m_name,m_password,m_tryCounter都是private的,因此User类的外部调用者无法调用它们,但请注意User类内部的方法比如SignIn或者属性Name,却完全可以调用。同样的,private方法IsValid,可以被SignIn方法调用,但对于外部调用者而言,则是无法调用的。而对于public属性Name,Password,public方法SignIn和 SignOut,外部的调用者是可以访问的。在后面的演练中,我们能够看出这之间的区别。通过这样分层次的封装,就可以充分保证对象的重用性和安全性。

那么对于类类型而言,如何确定它们的访问权限呢?这要根据实际的需求来看了。假定这个User类是用于一个电子商务网站。那么电子商务系统在设计过程中,就需要调用到User类对象。显然,登录与退出功能是必须提供给外部使用者的,例如登录页面就会使用到User类。而IsValid()方法用于验证用户的合法性,虽然也非常必要,但该功能仅仅用于登录的时候核实用户身份,也就是说,IsValid方法只会被SignIn方法使用,但外部实用者却并不关心,因此,设置为private就是合理的。同样的道理,字段m_tryCounter也是如此。但如果需求发生改变,验证用户的功能不仅仅是登录的时候需要使用,在添加商品到购物车,下订单,付款的时候,都需要该功能,那么IsValid方法,就有必要修改为public方法了。

所以,在设计程序的时候,除了要考虑识别对象,还要充分考虑该对象的封装。类对象内的字段、属性和方法,包括类本身,哪些应该暴露在外,哪些应该被隐藏,都需要根据实际的需求,给与正确的设计。

演练:
(一)设计类User,并调用该类
1、打开Visual Studio.Net,选择“File”菜单的“new”,选择“Project”;
2、选择Visual C# Projects中的“Console Application”。在Location中,定位你要保存的项目的路径,而名字则为“SecondExample”。该名字此时既是解决方案的名字,同时也是该项目的名字。
3、用鼠标右键单击项目名,在弹出的对话框中,将Assembly Name命名为SecondExample,将Default Namespace命名为:BruceZhang.com. SecondExample。
4、用鼠标右键单击项目名,选择“Add”菜单项的“Add Class”
5、在弹出的对话框中,将文件的名字命名为User.cs
6、点击“Open”按钮后,项目中就添加了一个新的文件User.cs。打开该文件,将public class User中的内容,修改为前面文中定义好的User类。
7、修改原有默认的Program.cs文件名为App.cs,然后将文件内容修改为:
 class App
 {
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [STAThread]
  static void Main(string[] args)
  {
   User user = new User();
  
   //用户名和密码均错误;
   user.Name = "Bruce";
   user.Password = "test";
   for (int i=0;i<=4;i++)
   {
    user.SignIn();
   }
   user.SignOut();

   //用户名正确,密码错误;
   user.Name = "admin";
   user.Password = "test";
   for (int i=0;i<=4;i++)
   {
    user.SignIn();
   }
   user.SignOut();

   //用户名和密码正确;
   user.Name = "admin";
   user.Password = "admin";
   for (int i=0;i<=4;i++)
   {
    user.SignIn();
   }
   user.SignOut();

   //注意此时是无法调用这样的字段和方法的;
   //user.m_name;
   //user.m_password;
   //user.IsValid();

   Console.ReadLine();
  }
 }
8、运行。

posted @ 2012-04-11 14:56 青蛙學堂 阅读(210) | 评论 (0)编辑 收藏

c#--继承

类可以从其他类中继承。这是通过以下方式实现的:在声明类时,在类名称后放置一个冒号,然后在冒号后指定要从中继承的类(即基类)。例如:

public class A
{
    public A() { }
}

public class B : A
{
    public B() { }
}

新类(即派生类)将获取基类的所有非私有数据和行为以及新类为自己定义的所有其他数据或行为。因此,新类具有两个有效类型:新类的类型和它继承的类的类型。

在上面的示例中,类 B 既是有效的 B,又是有效的 A。访问 B 对象时,可以使用强制转换操作将其转换为 A 对象。强制转换不会更改 B 对象,但您的 B 对象视图将限制为 A 的数据和行为。将 B 强制转换为 A 后,可以将该 A 重新强制转换为 B。并非 A 的所有实例都可强制转换为 B,只有实际上是 B 的实例的那些实例才可以强制转换为 B。如果将类 B 作为 B 类型访问,则可以同时获得类 A 和类 B 的数据和行为。对象可以表示多个类型的能力称为多态性。有关更多信息,请参见多态性(C# 编程指南)。有关强制转换的更多信息,请参见强制转换(C# 编程指南)

结构不能从其他结构或类中继承。类和结构都可以从一个或多个接口中继承。有关更多信息,请参见接口(C# 编程指南)

posted @ 2012-04-11 14:46 青蛙學堂 阅读(178) | 评论 (0)编辑 收藏

c#--多态

通过继承,一个可以用作多种类型:可以用作它自己的类型、任何基类型,或者在实现接口时用作任何接口类型。这称为多态性。C# 中的每种类型都是多态的。类型可用作它们自己的类型或用作 Object 实例,因为任何类型都自动将 Object 当作基类型。

多态性不仅对派生类很重要,对基类也很重要。任何情况下,使用类实际上都可能是在使用已强制转换为基类类型的派生类对象。基类的设计者可以预测到其基类中可能会在派生类中发生更改的方面。例如,表示汽车的基类可能包含这样的行为:当考虑的汽车为小型货车或敞篷汽车时,这些行为将会改变。基类可以将这些类成员标记为虚拟的,从而允许表示敞篷汽车和小型货车的派生类重写该行为。

有关更多信息,请参见继承

多态性概述

当派生类从基类继承时,它会获得基类的所有方法、字段、属性和事件。若要更改基类的数据和行为,您有两种选择:可以使用新的派生成员替换基成员,或者可以重写虚拟的基成员。

使用新的派生成员替换基类的成员需要使用 new 关键字。如果基类定义了一个方法、字段或属性,则 new 关键字用于在派生类中创建该方法、字段或属性的新定义。new 关键字放置在要替换的类成员的返回类型之前。例如:

public class BaseClass
{
    public void DoWork() { }
    public int WorkField;
    public int WorkProperty
    {
        get { return 0; }
    }
}

public class DerivedClass : BaseClass
{
    public new void DoWork() { }
    public new int WorkField;
    public new int WorkProperty
    {
        get { return 0; }
    }
}

使用 new 关键字时,调用的是新的类成员而不是已被替换的基类成员。这些基类成员称为隐藏成员。如果将派生类的实例强制转换为基类的实例,就仍然可以调用隐藏类成员。例如:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

为了使派生类的实例完全接替来自基类的类成员,基类必须将该成员声明为虚拟的。这是通过在该成员的返回类型之前添加 virtual 关键字来实现的。然后,派生类可以选择使用 override 关键字而不是 new,将基类实现替换为它自己的实现。例如:

public class BaseClass
{
    public virtual void DoWork() { }
    public virtual int WorkProperty
    {
        get { return 0; }
    }
}
public class DerivedClass : BaseClass
{
    public override void DoWork() { }
    public override int WorkProperty
    {
        get { return 0; }
    }
}

字段不能是虚拟的,只有方法、属性、事件和索引器才可以是虚拟的。当派生类重写某个虚拟成员时,即使该派生类的实例被当作基类的实例访问,也会调用该成员。例如:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Also calls the new method.

使用虚拟方法和属性可以预先计划未来的扩展。由于在调用虚拟成员时不考虑调用方正在使用的类型,所以派生类可以选择完全更改基类的外观行为。

无论在派生类和最初声明虚拟成员的类之间已声明了多少个类,虚拟成员都将永远为虚拟成员。如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以选择重写它,而不管类 B 是否为该成员声明了重写。例如:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

public class C : B
{
    public override void DoWork() { }
}

派生类可以通过将重写声明为密封的来停止虚拟继承。这需要在类成员声明中将 sealed 关键字放在 override 关键字的前面。例如:

public class C : B
{
    public sealed override void DoWork() { }
}

在上面的示例中,方法 DoWork 对从 C 派生的任何类都不再是虚拟的。它对 C 的实例仍然是虚拟的 -- 即使将这些实例强制转换为类型 B 或类型 A。派生类可以通过使用 new 关键字替换密封的方法,如下面的示例所示:

public class D : C
{
    public new void DoWork() { }
}

在此情况下,如果在 D 中使用类型为 D 的变量调用 DoWork,被调用的将是新的 DoWork。如果使用类型为 C、B 或 A 的变量访问 D 的实例,对 DoWork 的调用将遵循虚拟继承的规则,即把这些调用传送到类 C 的 DoWork 实现。

已替换或重写某个方法或属性的派生类仍然可以使用基关键字访问基类的该方法或属性。例如:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

public class C : B
{
    public override void DoWork()
    {
        // Call DoWork on B to get B's behavior:
        base.DoWork();

        // DoWork behavior specific to C goes here:
        // ...
    }
}









posted @ 2012-04-11 14:45 青蛙學堂 阅读(164) | 评论 (0)编辑 收藏

C#--属性

这是一种设计原则,称之为面向对象的封装性。从现在的开发角度说,是不建议直接将成员(指的是变量)直接用公开的方法供其它类访问的,因为这可能会导致有其它不可预见的时候意外修改了变量,或导致其它类似的问题。

当使用get、set将变量封装为属性以后,可以控制它们的可见性(能否被读取、能否被修改、能被谁操作等),修改的时候可以对将要修改的值进行验证是否合法,防止被其它类意外修改。这些都是面向对象设计的方法。

想知道详细的资料,可以搜索面向对象的相关特性:继承、封装和多态。

建议

关于C#中类的属性的一点理解

C#的class中大量使用属性,如
int speed;
int Speed
{
get{return speed;}
set{speed=value;}
}
本文将探讨以下几个问题
1 为什么要使用属性
2 使用属性带来的好处

关于为什么要使用属性:用于替代原C++或者其它编程语言中的存取器和变异器。
一直以来很多编程语言都是用存取器来实现对私有变量操控,比如SetSpeed()
及GetSpeed()这是两种方法,以前C++的写法是:
int speed;
int MaxSpeed=100;
bool SetSpeed(int s)
{
if(s<=MaxSpeed)
{
   speed=s;
   return ture;
}
else
   return false;
}
int GetSpeed()
{
if(speed>MaxSpeed)
    return MaxSpeed;
else
    return speed;
}
其实存取器的一个主要目的就是封装好,并做数据效验,而在C#中则可以直接通过
属性来实现同样的功能
int speed;
int MaxSpeed=100;
int Speed
{
set
{
   if(s<=MaxSpeed)
   {
      speed=s;
   }
}

get
{
   if(speed>MaxSpeed)
    {   return MaxSpeed;
   else
      return speed;
   }
}
}
可以看出其实C#中的属性也具备存取器的同样功能,而它编译成MSIL代码后也是一个方法;

使用属性带来的好处
1 为访问对象的内的实例变量及访问另一个对象中的实例变量提供了一致的语法
2 程序员可以节省检查一个实例变量是否为公有或私有,是否存在对此实例变量的存取器方法
3 每个需要被访问的实例变量通常会存在一个存取器,也就是要有Set及Get的方法,而实例的变量只需要一个属性。

另外当调用一个简单的get或set语句块时,编译器会执行特殊的优化,也就是内联(inline)
比如:
car.distance/Speed---->编译器会将此句转换成:car.distance/speed

posted @ 2012-04-11 14:39 青蛙學堂 阅读(200) | 评论 (0)编辑 收藏

仅列出标题
共43页: First 10 11 12 13 14 15 16 17 18 Last 
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(8)

随笔分类

随笔档案

收藏夹

青蛙学堂

最新评论

阅读排行榜

评论排行榜