葡京在线开户java在线聊天项目0.5本 解决客户端向劳动器端发送信息时只好发送一不成问题 OutputStreamWriter DataOutputStream socket.getOutputStream()

来源:http://www.oschina.net/translate/top-10-mistakes-that-c-sharp-programmers-make

从来不缓解问题之前客户端代码:

关于C#

package com.swift;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ChatClientFrame2 extends JFrame {

    private static final long serialVersionUID = -118470059355655240L;
    Socket s=null;
    JLabel label_shang=new JLabel();
    JLabel label_xia=new JLabel();
    JTextField tf = new JTextField(38);
    JTextArea ta = new JTextArea(15, 50);
    JButton button=new JButton();

    public ChatClientFrame2() {
        setBounds(200, 200, 500, 400);
        setTitle("客户端聊天工具 —— 0.4");
        //对窗口进行大的布局,分为三行一列,在pBasic面板上添加三个面板shang zhong xia
        JPanel pBasic=new JPanel();
        pBasic.setLayout(new BorderLayout());//不设置默认也是这种布局模式
        setContentPane(pBasic);//把面板放在窗口上,不记得用this.关键字
        JPanel shang=new JPanel();
        JPanel zhong=new JPanel();
        JPanel xia=new JPanel();
        //设置JPanel面板的大小
        shang.setSize(470, 25);
        zhong.setSize(470, 180);
        xia.setSize(470, 40);
        pBasic.add(shang,BorderLayout.NORTH);
        pBasic.add(zhong,BorderLayout.CENTER);
        pBasic.add(xia,BorderLayout.SOUTH);
        shang.setBackground(Color.red);
        zhong.setBackground(Color.yellow);
        xia.setBackground(Color.blue);
        /*
         * 三个面板,上边放一个标签“聊天记录”,中间放一个文本域,
         * 下边分为左中右——分别放标签“输入信息“,文本框和”发送“按钮
         */
        label_shang.setText("聊天记录");
        shang.add(label_shang);
        ta.setLineWrap(true);// 自动换行
        JScrollPane scroll=new JScrollPane(ta);// 增加滚动条,以便不增加行数
        zhong.add(scroll);
        label_xia.setText("输入信息");
        xia.add(label_xia,BorderLayout.WEST);
        /*
         * 增加功能,窗口监听事件,窗口打开时设置光标焦点在tf文本域中
         */
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                tf.requestFocus();
            }
        });
        xia.add(tf,BorderLayout.CENTER);
        button.setText("发送");
        xia.add(button,BorderLayout.EAST);
        /*
         * 增加“发送”按钮的功能,增加回车的功能,监听相同,
         * 则使用内部类实现,以提高代码复用性
         */
        final class ShareListener implements ActionListener{

            @Override
            public void actionPerformed(ActionEvent e) {
                String taText=ta.getText();
                String tfText=tf.getText()+"\r\n";
                ta.setText(taText+tfText);
                tf.setText("");
                //当回车或发送按钮时,tfText发送到服务器
                try {
                    //可以尝试用DataOutputStream里的writeUTF方法
//                    DataOutputStream ds=new DataOutputStream(s.getOutputStream());
//                    ds.writeUTF(tfText);
//                    ds.flush();
//                    ds.close();
                    BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream(),"utf-8"));
                    bw.write(tfText);
                    bw.close();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }

            }
        }
        button.addActionListener(new ShareListener());
        tf.addActionListener(new ShareListener());
        //通过压缩自动调整各个面板
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 点关闭按钮同时退出程序
        setVisible(true);
        //创建窗体直接调用连接服务器
        connect();
    }

    /*
     * 增加一个连接到服务器的方法
     */
    public void connect() {
        try {
            s=new Socket("127.0.0.1",8888);
            System.out.println("connected!");
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 别忘了创建窗体对象,也可以用生成对象调用其他的方法如launchFrame()
        new ChatClientFrame2();
    }

}

C#是上微软官语言运行库(CLR)的个别言语中的如出一辙种。达成CLR的言语可以受益于该带来的特征,如跨语言集成、异常处理、安全性增强、部件组合的简易模型与调节和分析服务。作为当代底CLR语言,C#大凡用最常见的,其行使场景针对Windows桌面、移动手机及服务器环境等繁杂、专业的支付项目。

靡解决问题之前服务端代码:

C#是种植面向对象的强类型语言。C#每当编译和周转时还有些强类型检查,使在大多数卓越的编程错误能够吃尽早地觉察,而且位置一定一定精准。相比叫那些不拘泥类型,在违规操作很遥远后才报发出而追踪到莫名其妙错误的言语,这足以吧程序员节省成千上万时刻。然而,许多程序员有意要下意识地扔了这检测的粗,这致使本文中讨论的有题目。

package com.swift;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {

    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8888);
            for (;;) {
                Socket s = ss.accept();
                //当有连接,则显示,用于测试
                System.out.println("a client connected success");
                //可以尝试用DataInputStream中readUTF方法
//                DataInputStream dis=new DataInputStream(s.getInputStream());
//                String str=dis.readUTF();
//                dis.close();

                BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream(),"utf-8"));
                String str=br.readLine();
                System.out.println(str);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

至于本文

演示过程

正文描述了10独 C# 程序员常犯的不当,或该避免的钩。

葡京在线开户 1

尽管本文讨论的绝大多数错是指向 C# 的,有些错误以及其他因 CLR
为目标的语言,或者采取了 Framework Class Library (FCL) 的语言为连带。

起问题:


java.net.SocketException: Socket is closed

广大错误 #1: 把援当做值来用,或者转

葡京在线开户 2

C++
和另很多言语的程序员,习惯了为变量赋值的上,要么赋单纯的价值,要么是现有对象的援。然而,在C#
中,是价值还是援引,是出于写是目标的程序员决定的,而休是实例化对象并赋值的程序员决定的。这频繁会坑至
C# 的初手程序员。

 

倘若您切莫知晓乃方使用的对象是不是是值类型或引用类型,你或许会见遇到有惊喜。例如:


  Point point1 = new Point(20, 30);
  Point point2 = point1;
  point2.X = 50;
  Console.WriteLine(point1.X);       // 20 (does this surprise you?)
  Console.WriteLine(point2.X);       // 50

  Pen pen1 = new Pen(Color.Black);
  Pen pen2 = pen1;
  pen2.Color = Color.Blue;
  Console.WriteLine(pen1.Color);     // Blue (or does this surprise you?)
  Console.WriteLine(pen2.Color);     // Blue

 如果未牵扯闭流

比方您所表现,尽管Point和Pen对象的始建方式相同,但是当一个初的X的坐标值被分配到point2常常,
point1的价保持无转移
。而当一个新的color值被分配至pen2,pen1也随后变动。因此,我们可测算point1和point2每个都富含自己之Point对象的副本,而pen1和pen2引用了同一个Pen对象
。如果没此测试,我们怎么能知情这原理?

把立即句删掉bw.close();

同样种方式是错开押一下靶是怎样定义的(在Visual
Studio中,你得将光标放在对象的讳上,并遵照下F12键)

大凡好的,程序不会见拧,但说到底要关张,可以当客户端窗口关闭的时节实施就词。

  public struct Point { … }     // defines a “value” type
  public class Pen { … }        // defines a “reference” type

比方达到所示,在C#饱受,struct关键字是为此来定义一个值类型,而class关键字是因此来定义引用类型的。
对于那些有C++编程背景人来说,如果让C++和C#中某些类似的重中之重字为混,可能会见指向上述这种表现感到老受惊。

 

只要您想要拄之行会坐值类型和援类型而异,举例来说,如果您想拿一个对象作为参数传被一个办法,并于是法子被改是目标的状态。你一定要确保您以处理是的类对象。

后续运行发现还是只能发送一长达信息,但尚无错提示。这时为什么吗?


翻开程序意识,当每次点击回车或者发送按钮,都见面生同样修消息发送,但服务器即没有显得,因为客户端发送了往往,服务端只读取了同一次,所以展示平长达,

周边的不当#2:误会未初始化变量的默认值

下代码是读取多次,显示多次:

在C#吃,值得类型不克为空。根据定义,值的类别值,甚至初始化变量的值类型必须出一个价。这就是是所谓的拖欠品种的默认值。这一般会导致以下,意想不到的结果经常,检查一个变量是否不初始化:

package com.swift;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {

    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8888);
            for (;;) {
                Socket s = ss.accept();
                //当有连接,则显示,用于测试
                System.out.println("a client connected success");

                BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream(),"utf-8"));
                //问题:客户端多次发送信息,而服务器端只读取了一次,所以只输出一次,多次输出下面的办法也不好
                String str=br.readLine();
                System.out.println(str);
                String str1=br.readLine();
                System.out.println(str1);
                String str2=br.readLine();
                System.out.println(str2);
                String str3=br.readLine();
                System.out.println(str3);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  class Program {
      static Point point1;      static Pen pen1;      static void Main(string[] args) {
          Console.WriteLine(pen1 == null);      // True
          Console.WriteLine(point1 == null);    // False (huh?)
      }
  }

 接收了季不成

缘何非是【point
1】空?答案是,点是一个值类型,和默认值点(0,0)一样,没有空值。
当即是一个非常简单和大的失实。在C#受到许多(但是非是普)值类型有一个【IsEmpty】属性,你可以看看她是否等于默认值:

可加一个死循环来最为接收

Console.WriteLine(point1.IsEmpty);        // True

葡京在线开户 3


成解决接收次数问题,可后止的关闭不克履行了

常见错误 #3: 使用非对路或无指定的主意比较字符串

改服务端代码如下:

在C#丁生出成百上千方法来比较字符串。

葡京在线开户 4

则发许多程序员使用==操作符来比字符串,但是这种方法其实是最好无引进应用的。主要缘由是由于这种措施没有以代码中显式地指定使用啊种类型去于字符串。
相反,在C#遭受判断字符串是否当最好用Equals方法:


public bool Equals(string value);  



public bool Equals(string value, StringComparison comparisonType);

 

首先单Equals方法(没有comparisonType这参数)和利用==操作符的结果是同样的,但利益是,它显式的指明了于类型。它见面以顺序逐字节的失于字符串。在众动静下,这多亏你所盼的于类型,尤其是当于有由此编程设置的字符串,像文件称,环境变量,属性等。在这些情形下,只要遵循顺序逐字节的较就好了。使用未牵动comparisonType参数的Equals方法进行比较的绝无仅有不好的地方在那些读你程序代码的食指想必不清楚您的较类型是啊。

0.5版本效果图如下:

运带来comparisonType的Equals方法去于字符串,不仅会如您的代码更清晰,还会见要你错过考虑清楚而用哪种类型去比字符串。这种艺术好值得您去下,因为尽管以英语受到,按顺序进行的比较和按语言区域展开的较中并无太多之界别,但是以任何的有些语种可能会见产生不行十分之差。如果您不经意了这种可能性,无疑是啊汝自己以未来的征程上凿了过多“坑”。举例来说:

葡京在线开户 5

  string s = "strasse";

  // outputs False:
  Console.WriteLine(s == "straße");
  Console.WriteLine(s.Equals("straße"));
  Console.WriteLine(s.Equals("straße", StringComparison.Ordinal));
  Console.WriteLine(s.Equals("Straße", StringComparison.CurrentCulture));        
  Console.WriteLine(s.Equals("straße", StringComparison.OrdinalIgnoreCase));

  // outputs True:
  Console.WriteLine(s.Equals("straße", StringComparison.CurrentCulture));
  Console.WriteLine(s.Equals("Straße", StringComparison.CurrentCultureIgnoreCase));

0.5本子服务端代码:

不过安全之推行是连接为Equals方法提供一个comparisonType的参数。

package com.swift;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {

    public static void main(String[] args) {
        boolean started=false;
        try {
            ServerSocket ss = new ServerSocket(8888);
            started=true;
            while(started) {
                boolean connected=false;
                Socket s = ss.accept();
                System.out.println("a client connected success");
                connected=true;
                BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream(),"utf-8"));
                while(connected) {
                String str=br.readLine();
                System.out.println(str);
                }
                br.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

下是有些着力的点拨规范:

0.5版客户端代码:(增加disconnect()方法,放入输出流和端口的关门,优化输出流创建次数)

  • 当于用户输入的字符串或者用字符串比较结实显示被用户时时,使用本地化的比较(CurrentCulture
    或者CurrentCultureIgnoreCase)。
  • 当用于程序设计之于字符串时,使用原的可比(Ordinal 或者
    OrdinalIgnoreCase)
  • InvariantCulture和InvariantCultureIgnoreCase一般并无下,除非在受限的情境之下,因为原本之可比平常效率又强。如果与本土文化有关的比较是不可或缺的,它应吃实施成基于当下底学识要其它一样种植新鲜文化的可比。
package com.swift;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ChatClientFrame extends JFrame {

    private static final long serialVersionUID = -118470059355655240L;
    Socket s=null;
    BufferedWriter bw=null;
    JLabel label_shang=new JLabel();
    JLabel label_xia=new JLabel();
    JTextField tf = new JTextField(38);
    JTextArea ta = new JTextArea(15, 50);
    JButton button=new JButton();

    public ChatClientFrame() {
        setBounds(200, 200, 500, 400);
        setTitle("客户端聊天工具 —— 0.5");
        //对窗口进行大的布局,分为三行一列,在pBasic面板上添加三个面板shang zhong xia
        JPanel pBasic=new JPanel();
        pBasic.setLayout(new BorderLayout());//不设置默认也是这种布局模式
        setContentPane(pBasic);//把面板放在窗口上,不记得用this.关键字
        JPanel shang=new JPanel();
        JPanel zhong=new JPanel();
        JPanel xia=new JPanel();
        //设置JPanel面板的大小
        shang.setSize(470, 25);
        zhong.setSize(470, 180);
        xia.setSize(470, 40);
        pBasic.add(shang,BorderLayout.NORTH);
        pBasic.add(zhong,BorderLayout.CENTER);
        pBasic.add(xia,BorderLayout.SOUTH);
        shang.setBackground(Color.red);
        zhong.setBackground(Color.yellow);
        xia.setBackground(Color.blue);

        label_shang.setText("聊天记录");
        shang.add(label_shang);
        ta.setLineWrap(true);// 自动换行
        JScrollPane scroll=new JScrollPane(ta);// 增加滚动条,以便不增加行数
        zhong.add(scroll);
        label_xia.setText("输入信息");
        xia.add(label_xia,BorderLayout.WEST);
        /*
         * 增加功能,窗口监听事件,窗口打开时设置光标焦点在tf文本域中
         */
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                tf.requestFocus();
            }
        });
        xia.add(tf,BorderLayout.CENTER);
        button.setText("发送");
        xia.add(button,BorderLayout.EAST);

        final class ShareListener implements ActionListener{

            @Override
            public void actionPerformed(ActionEvent e) {
                String taText=ta.getText();
                String tfText=tf.getText()+"\r\n";
                ta.setText(taText+tfText);
                tf.setText("");
                //当回车或发送按钮时,tfText发送到服务器
                try {
                    bw.write(tfText);
                    bw.flush();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

            }
        }
        button.addActionListener(new ShareListener());
        tf.addActionListener(new ShareListener());
        //通过压缩自动调整各个面板
        pack();
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                //disconnect();//出错
                System.exit(0);
            }
        });
        setVisible(true);
        //创建窗体直接调用连接服务器
        connect();
    }

    public void connect() {
        try {
            s=new Socket("127.0.0.1",8888);
            System.out.println("connected!");
            bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream(),"utf-8"));

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void disconnect() {
        try {
            s.close();
            bw.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        new ChatClientFrame();
    }

}

此外,对Equals
方法吧,字符串也常见提供了Compare方法,可以供字符串的对立顺序信息如果不仅测试是否等于。这个艺术好生好适用于<,
<=, >和>= 运算符,对上述议论同样适用。

 


周边误区 #4: 使用迭代式 (而非是声明式)的语句去操作集合

在C#
3.0中,LINQ的引入改变了咱们以往对聚集对象的询问以及改操作。从这以后,你该用LINQ去操作集合,而未是透过迭代底方式。

一些C#的程序员甚至还不理解LINQ的留存,好以匪懂得之丁在渐渐回落。但是还发来人误以为LINQ只所以当数据库查询中,因为LINQ的机要字与SQL语句其实是不过像了。

虽说数据库的查询操作是LINQ的一个格外典型的动,但是它同好运用叫各种可枚举的集对象。(如:任何实现了IEnumerable接口的对象)。举例来说,如果您发出一个Account类型的累组,不要写成下面这样:

decimal total = 0;  
foreach (Account account in myAccounts) 
{    
     if (account.Status == "active") 
     {
        total += account.Balance;
     }
}

汝而这么形容:

  decimal total = (from account in myAccounts
               where account.Status == "active"
               select account.Balance).Sum();

虽这是一个很简单的事例,在稍情况下,一个十足的LINQ语句子可以随意地更迭掉你代码中一个迭代循环(或嵌套循环)里之几十条语句。更不见的代码通常意味着有Bug的机会吗会再度少地让引入。然而,记住,在性方面可能只要权一下。在性质非常重点的观,尤其是您的迭代代码能够对而的会师进行假设时,LINQ做不至,所以自然要在马上有限种方法之间比较一下性能。


#5常常表现错:在LINQ语句之中无考虑底层对象

对于拍卖抽象操纵集合任务,LINQ无疑是巨之。无论他们是当内存的靶子,数据库表,或者XML文档。在这样一个圆世界里,你切莫需要掌握底层对象。然而一旦我们生存在一个两全世界中间可能会见带错误。事实上,当以标准的平数量及实行时,如果该数额碰巧在一个不等的格式之中相同的,LINQ语句能回到不同之结果

如,请考虑下的话语:

decimal total=(from accout in myaccouts
    where accout.status=='active'
    select accout.Balance).sum();

想像一下,该目标某之账号会生出什么。状态相当“Active”(注意大写A)?

吓吧,如果myaccout是Dbset的靶子。(默认设置了不同组别轻重缓急写的配置),where表达式仍会配合该因素。然而,如果myaccout是以内存阵列之中,那么它以不般配,因此拿起不同之究竟的结果。

当一会,在咱们前面讨论了之字符串比较受到, 我们看见 ==
操作符扮演的角色就是略的较. 所以,为什么以斯标准下, ==
表现有之凡另外的一个款式呢 ?

答案是,当以LINQ语句被之底蕴对象还引用到SQL表中的数量(如与于这例子中,在实体框架为DbSet的目标的情状下),该语句被换成为一个T-SQL语句。然后照的T-SQL的平整,而非是C#的条条框框,所以当上述情况下之较了是匪区分轻重缓急写的。

诚如情形下,即使LINQ是一个方便之以及同样的法来询问对象的聚合,在实际中您还亟需理解您的语是否会被翻成什么,来确保您的代码的所作所为将使预期运行。


广泛错误 #6:对扩大方法感到纳闷或叫其的款式诈骗

像先前关系的,LINQ状态依赖让IEnumerable接口的贯彻目标,比如,下面的概括函数会协商帐户汇中的帐户余额:

public decimal SumAccounts(IEnumerable<Account> myAccounts) 
{      
   return myAccounts.Sum(a => a.Balance);
}

每当面的代码中,myAccounts参数的类型为声称也IEnumerable<Account>,myAccounts引用了一个Sum
方法 (C# 使用类之 “dot notation”
引用方法还是接口中的好像),我们要在IEnumerable接口中定义一个Sum()方法。但是,IEnumerable没有为Sum方法供任何引用并且只有如下所显示之凝练定义:

 public interface IEnumerable<out T> : IEnumerable 
 {
      IEnumerator<T> GetEnumerator();
 }

可是Sum方法应该定义及何处?C#大凡强类型的语言,因此若Sum方法的援是低效的,C#编译器会针对其报错。我们理解它们必须存在,但是当于哪也?此外,LINQ提供的供查询以及集合结果有方以哪定义也?

答案是Sum并无以IEnumerable接口内定义,而是一个

定义在System.Linq.Enumerable类中的static方法(叫做“extension method”)

 namespace System.Linq 
 {
    public static class Enumerable 
    {      ...
      // the reference here to “this IEnumerable<TSource> source” is
      // the magic sauce that provides access to the extension method Sum
      public static decimal Sum<TSource>(this IEnumerable<TSource> source,Func<TSource, decimal> selector);      ...
    }
 }

推而广之方法的显著特点是率先个形参前的this修饰符。这即是编译器知道她是一个扩大方法的“奥妙”。它所修饰的参数的路(这个例子中的IEnumerable)说明这个近乎还是接口将显得实现了之法。

(另外需要指出的是,定义扩展方法的IEnumerable接口和Enumerable类的名中的相似性没什么奇怪之。这种相似性只是随意的风格选择。)

明了当时一点,我们可见见地方介绍的sumAccounts方法能够因为下面的方法贯彻:

  public decimal SumAccounts(IEnumerable<Account> myAccounts) 
  {
      return Enumerable.Sum(myAccounts, a => a.Balance);
  }

实在我们或就这么实现了是方法,而休是问什么而产生扩张方法。扩展方法本身只是是C#的一个方便而管需后续、重新编译或者修改原始代码就可于曾经怀的当路“添加”方法的计。

扩大方法通过在文件开始添加using
[namespace];引入到作用域。你待懂得你只要寻找的扩张方法所在的名空间。如果你掌握乃要是物色的是呀,这点老爱。

当C#编译器碰到一个目标的实例调用了一个方式,并且它在是目标的切近中找寻不顶很方式,它便会尝试以作用域中颇具的扩充方法里搜寻一个匹配所要求的类和艺术签名的。如果找到了,它便管实例的援当做第一独参数传给大扩展方法,然后要发生另外参数的语,再把她依次传入扩展方法。(如果C#编译器没有以作用域中找到相应的扩张方法,它见面抛措。)

对C#编译器来说,扩展方法是单“语法糖”,使我们能把代码写得重清,更爱维护(多数状态下)。显然,前提是公懂它们的用法,否则,它见面比易于吃人口迷惑,尤其是相同开始。

行使扩展方法真的发优势,但为会见吃那些针对她不了解还是认识不科学的开发者头疼,浪费时间。尤其是当羁押于线示例代码,或者其它都勾勒好的代码的时。当这些代码来编译错误(因为她调用了那些显然并未当受调用类型中定义之方法),一般的赞同是考虑代码是否利用叫所引述类库的其余版本,甚至是差之类库。很多工夫会受消费在寻觅新本子,或者吃看“丢失”的类库上。

以扩展方法的讳与好像中定义之道的名一样,只是当术签名上闹一线区别的时节,甚至那些熟悉扩展方法的开发者也偶尔犯上面的失实。很多时日会见让消费在探寻“不存在”的拼写错误上。

在C#面临,用扩展方法换得尤为流行。除了LINQ,在另外两单来源微软今于广泛采用的类库Unity
Application Block和Web API
framework中,也祭了扩大方法,而且还有好多别样的。框架进一步新,用扩展方法的可能性越来越老。

当然,你为堪写你自己之壮大方法。但是必须意识及虽然扩大方法看起来与另外实例方法一致叫调用,但这实际上只是幻觉。事实上,扩展方法无能够看所加大展类的民用和维护成员,所以它们不能够叫当传统延续的替代品。


广阔错误 #7: 对手头上的任务采取不当的聚众类型

C#提供了汪洋底集纳类型的对象,下面就排有了间的同组成部分:

Array,ArrayList,BitArray,BitVector32,Dictionary<K,V>,HashTable,HybridDictionary,List<T>,NameValueCollection,OrderedDictionary,Queue, Queue<T>,SortedList,Stack, Stack<T>,StringCollection,StringDictionary.

可以多少情况下,有极度多的挑和尚未足够的选同一糟糕,集合类型为是如此。数量众多的选项余地肯定可以保是您的干活健康运转。但是你不过好要花片岁月提前查找并询问一下会合类型,以便选择一个极致可您需要的聚集类型。这最终会使你的主次性能更好,减少失误的或者。

如果产生一个成团指定的元素类型(如string或bit)和而正在操作的同一,你无比好优先选项使用它们。当指定相应的要素类型时,这种集的频率还胜。

为利用好C#受到之类别安全,你最好好选以一个泛型接口,而未是以非泛型的借口。泛型接口中之素类型是你在以宣称对象时指定的种,而未泛型中的元素是object类型。当用一个非泛型的接口时,C#的编译器不克对而的代码进行项目检查。同样,当您以操作原生类型的集结时,使用非泛型的接口会招致C#针对这些项目进行频繁之装箱(boxing)和拆箱(unboxing)操作。和动指定了适度类型的泛型集合相比,这会带好显眼的性影响。

外一个广大的圈套是友善去实现一个成团类型。这并无是说永远不要这么做,你可透过动用还是扩展.NET提供的片段被普遍应用的汇类型来节省大量底日,而非是失去又过去轮子。
特别是,C#的C5 Generic Collection Library
和CLI提供了广大分外的集合类型,像持久化树形数据结构,基于堆的先期级列,哈希索引的数组列表,链表等及重复多。


周边错误#8:遗漏资源自由

CLR
托管环境去了垃圾堆回收器的角色,所以若免需要显式释放已创建对象所占据的内存。事实上,你为无克显式释放。C#遭没有跟C++
delete对应的运算符或者和C语言中free()函数对应的办法。但眼看并无表示你可以忽略所有的下了之目标。许多目标类型封装了重重其他种类的系统资源(例如,磁盘文件,数据连接,网络端口等等)。保持这些资源使状态会激烈耗尽系统的资源,削弱性能并且最后造成程序出错。

尽管所有C#的接近吃还定义了析构方法,但是销毁对象(C#受吗称终结器)可能存在的问题是您免确定她于上吃调用。他们当未来一个请勿确定的光阴被垃圾回收器调用(一个异步的线程,此举也许引发额外的起)。试图避免这种由垃圾回收器中GC.Collect()方法所施加的要挟限制并非同一种植好的编程实践,因为可能于垃圾回收线程试图回收适宜回收的对象时,在不可预知的辰外造成线程阻塞。

立马并表示最不用就此终结器,显式释放资源并无会见促成其中的旁一个结果。当您打开一个文书、网络端口或者数连接时,当你不再采取这些资源时,你应当尽早的显式释放这些资源。

资源泄露几乎在有的环境遭受还见面引发关注。但是,C#供了扳平栽健康的体制而资源的下变得简单。如果成立利用,可以大大减少泄露出现的机率。NET
framework定义了一个IDisposable接口,仅出于一个Dispose()构成。任何实现IDisposable的接口的靶子都见面于靶生命周期结束调用Dispose()方法。调用结果肯定以决定性的假释占用的资源。

设当一个代码段遭遇开创并释放一个靶,却遗忘调用Dispose()方法,这是不可原谅的,因此C#供了using语句以管教无代码以什么的办法退出,Dispose()方法还见面让调用(不管是异常,return语句,或者略的代码段竣工)。这个using和之前提到的于文书开始用来引入名字空间的一致。它发生另外一个博C#开发者都未曾发觉的,完全不相干的目的,也不怕是包代码退出时,对象的Dispose()方法吃调用:

  using (FileStream myFile = File.OpenRead("foo.txt")) {
    myFile.Read(buffer, 0, 100);
  }

在上面示例中应用using语句,你就是足以确定myFile.Dispose()方法会在文件使用了以后为随即调用,不管Read()方法发生没来遗弃大。


大规模错误 #9: 回避异常

C#当运行时也会强制进行项目检查。相对于诸如C++这样会受左的类型转换赋一个随机值的语言来说,C#立即得假设你再次快的找到出错的岗位。然而,程序员再同糟糕无视了C#的就等同特色。由于C#提供了少种植档次检查的主意,一种会丢掉来非常,而别一样种植则非会见,这很可能会见要他们丢失进是“坑”里。有些程序员倾向于回避异常,并且认为不写
try/catch 语句可以省去一些代码。

例如,下面演示了C#吃进行展示类型转换的少数种不同的方式:

  // 方法 1:
  // 如果 account 不能转换成 SavingAccount 会抛出异常
  SavingsAccount savingsAccount = (SavingsAccount)account;

  // 方法 2:
  // 如果不能转换,则不会抛出异常,相反,它会返回 null
  SavingsAccount savingsAccount = account as SavingsAccount;

特别鲜明,如果不对方法2回到的结果开展判定的话,最终大可能会见生一个
NullReferenceException
的怪,这也许会见现出于有些晚几的时,这令问题更难以追踪。对比的话,方法1会这抛弃来一个
InvalidCastExceptionmaking,这样,问题之源于就是够呛强烈了。

除此以外,即使你知道要对方法2的归值进行判断,如果你发现价值也空,接下你会怎么开?在斯方法吃晓错误合适与否?如果类型转换失败了而还发生另外的法门去品味吧?如果没的话,那么抛出这个非常是唯一正确的精选,并且颇的遗弃来点距离那产生点越来越临近越好。

下的事例演示了其它一律组广泛的不二法门,一栽会丢来怪,而其他一样种植则不见面:

  int.Parse();     // 如果参数无法解析会抛出异常
  int.TryParse();  // 返回bool值表示解析是否成功

  IEnumerable.First();           // 如果序列为空,则抛出异常
  IEnumerable.FirstOrDefault();  // 如果序列为空则返回 null 或默认值

小程序员认为“异常有害”,所以他们自然而然的看无遏来好的程序显得越“高大上”。虽然于一些情况下,这种意见是不利的,但是这种观点并无适用于具有的景况。

选个实际的事例,某些情况下当大来时,你发另外一个可选的方法(如,默认值),那么,选用不丢掉来老的办法是一个于好之选取。在这种情景下,你不过接近下面这样勾画:

  if (int.TryParse(myString, out myInt)) 
  {
      // use myInt
  }
  else 
  {    
      // use default value
  }

若不是如此:

  try 
  {
    myInt = int.Parse(myString);    // use myInt
  }
  catch (FormatException) 
    // use default value
  }

唯独,这并无说明 TryParse
方法重新好。某些情况下适合,某些情况下虽非适合。这即是干吗起个别种植办法供我们摘了。根据你的具体情况选择适用的方式,并记住,作为一个开发者,异常是全然好变成您的冤家的。


广大错误 #10: 累积编译器警告而休处理

此似是而非并无是C#所特有的,但是当C#饱受这种景象倒是于多,尤其是从C#编译器弃用了严酷的品类检查过后。

警戒的面世是发生原因的。所有C#的编译错误还标明你的代码有弱点,同样,一些警戒吗是如此。这两者之间的区分在于,对于警告的话,编译器可以按照你代码的指令工作,但是,编译器发现而的代码来同点多少题目,很有或会见使您的代码不能够按照你的预料运行。

一个宽广的例子是,你改改了而的代码,并移除了针对性少数变量的动,但是,你忘记了移除了该变量的声明。程序可以老好的运转,但是编译器会唤起有无利用的变量。程序可以死好之周转使得部分程序员不错过修补警告。更有甚者,有些程序员很好之运用了Visual
Studio中“错误列表”窗口的隐藏警告的效能,很轻之尽管拿警告过滤了,以便专注让左。不用多长时间,就见面累积一堆积警告,这些警告都受“惬意”的不经意了(更不好的凡,隐藏掉了)。

但是,如果你忽视掉这等同类似的警告,类似于下这例子迟早会产出于你的代码中。

  class Account 
  {

      int myId;      
      int Id;   // 编译器已经警告过了,但是你不听

      // Constructor
      {          
         this.myId = Id;     // OOPS!
      }

  }

再次增长以了编辑器的智能感知的功能,这种错误就不行有或出。

现,你的代码中有矣一个重的失实(但是编译器只是出口了一个警戒,其原因现已讲了),这会浪费你大量之年华错开摸索这错,具体情况由乃的次复杂程度决定。如果您平开始即注意到了这个警示,你只是需要5秒钟就足以修改掉,从而避免这个题材。

牢记,如果你细心看的说话,你见面发觉,C#编译器给了而多多关于您程序健壮性的管事之信息。不要忽视警告。你一味需要花几秒钟的流年纵可修复它们,当出现的时光就是失去修复它,这足以呢而节省成千上万时日。试着啊投机塑造同样种植“洁癖”,让Visual
Studio 的“错误窗口”一直亮“0荒唐,
0警告”,一旦出现警示便感到不爽快,然后就把警告修复掉。

当了,任何规则都发出不同。所以,有些时候,虽然你的代码在编译器看来是小问题的,但是这多亏你想只要之。在这种很少见的场面下,你无与伦比好下
#pragma warning disable [warning id]
把吸引警告的代码包裹起来,而且只是包警告ID对应之代码。这会还仅见面抑制对应之警示,所以当有新的警告来的下,你还是会知晓的。.
总结

C#举凡平等流派强大的同时十分利索的言语,它起过多体制同语言专业来家喻户晓的增进而的生产力。和另语言同样,如果对它们能力的垂询一点儿,这特别可能会见被你带阻碍,而非是好处。正而一词谚语所说之那么“knowing
enough to be
dangerous”(译者注:意思是自以为已经了解足够了,可以做某事了,但实质上不是)。

熟悉C#的有些要的一线之处,像本文中所涉的那些(但不制止这些),可以帮忙我们更好的去用语言,从而避免有广阔的陷阱。