Các tính năng ẩn của C #? [đóng cửa]


1475

Điều này xuất hiện trong đầu tôi sau khi tôi học được những điều sau đây từ câu hỏi này :

where T : struct

Chúng tôi, các nhà phát triển C #, tất cả đều biết những điều cơ bản của C #. Ý tôi là khai báo, điều kiện, vòng lặp, toán tử, v.v.

Một số người trong chúng ta thậm chí còn thành thạo các công cụ như Generics , các loại ẩn danh , lambdas , LINQ , ...

Nhưng những tính năng hay thủ thuật ẩn giấu nhất của C # mà ngay cả người hâm mộ, người nghiện, chuyên gia C # cũng không biết?

Dưới đây là các tính năng được tiết lộ cho đến nay:


Từ khóa

Thuộc tính

Cú pháp

  • ??Toán tử (hợp nhất null) bởi kokos
  • Cờ hiệu số của Nick Berardi
  • where T:newbởi Lars Mæhlum
  • Khái quát tiềm ẩn của Keith
  • Lambdas một tham số của Keith
  • Thuộc tính tự động của Keith
  • Bí danh không gian tên của Keith
  • Nguyên văn chuỗi ký tự với @ by Patrick
  • enumgiá trị của lfoust
  • @variablenames bởi marxidad
  • eventkhai thác bởi marxidad
  • Định dạng chuỗi ngoặc theo Portman
  • Công cụ sửa đổi khả năng truy cập thuộc tính của xanadont
  • Toán tử có điều kiện (ternary) ( ?:) của JasonS
  • checkedvà các uncheckednhà khai thác bởi Binoj Antony
  • implicit and explicitkhai thác bởi Flory

Đặc điểm ngôn ngữ

Tính năng Visual Studio

  • Chọn khối văn bản trong trình soạn thảo của Himadri
  • Đoạn trích của DanielSmurf

Khung

Phương thức và tính chất

  • String.IsNullOrEmpty()phương pháp của KiwiBastard
  • List.ForEach()phương pháp của KiwiBastard
  • BeginInvoke(), EndInvoke()phương pháp của Will Dean
  • Nullable<T>.HasValueNullable<T>.Valuetài sản của Rismo
  • GetValueOrDefaultphương pháp của John Sheehan

Mẹo và thủ thuật

  • Phương pháp hay cho người xử lý sự kiện của Andreas HR Nilsson
  • So sánh chữ hoa của John
  • Truy cập các loại ẩn danh mà không có sự phản ánh của dp
  • Một cách nhanh chóng để nhanh chóng khởi tạo các thuộc tính bộ sưu tập của Will
  • Các hàm nội tuyến ẩn danh giống như JavaScript của roosteronacid

Khác

Câu trả lời:


752

Đây không phải là C # per se, nhưng tôi chưa thấy ai thực sự sử dụng System.IO.Path.Combine()đến mức họ nên làm. Trong thực tế, toàn bộ lớp Path thực sự hữu ích, nhưng không ai sử dụng nó!

Tôi sẵn sàng đặt cược rằng mọi ứng dụng sản xuất đều có mã sau, mặc dù không nên:

string path = dir + "\\" + fileName;

584

lambdas và suy luận loại được đánh giá thấp. Lambdas có thể có nhiều câu lệnh và chúng tự động nhân đôi thành một đối tượng ủy nhiệm tương thích (chỉ cần đảm bảo khớp chữ ký) như trong:

Console.CancelKeyPress +=
    (sender, e) => {
        Console.WriteLine("CTRL+C detected!\n");
        e.Cancel = true;
    };

Lưu ý rằng tôi không phải new CancellationEventHandlercũng không phải chỉ định các loại sendere, chúng không thể tham gia được từ sự kiện. Đó là lý do tại sao điều này ít rườm rà hơn khi viết toàn bộ delegate (blah blah)cũng đòi hỏi bạn phải chỉ định các loại tham số.

Lambdas không cần trả lại bất cứ điều gì và kiểu suy luận cực kỳ mạnh mẽ trong bối cảnh như thế này.

Và BTW, bạn luôn có thể trả về Lambdas tạo ra Lambdas theo nghĩa lập trình chức năng. Ví dụ: đây là lambda tạo ra lambda xử lý sự kiện Button.Click:

Func<int, int, EventHandler> makeHandler =
    (dx, dy) => (sender, e) => {
        var btn = (Button) sender;
        btn.Top += dy;
        btn.Left += dx;
    };

btnUp.Click += makeHandler(0, -1);
btnDown.Click += makeHandler(0, 1);
btnLeft.Click += makeHandler(-1, 0);
btnRight.Click += makeHandler(1, 0);

Lưu ý chuỗi: (dx, dy) => (sender, e) =>

Bây giờ, đó là lý do tại sao tôi rất vui khi tham gia lớp lập trình chức năng :-)

Khác với các con trỏ trong C, tôi nghĩ đó là điều cơ bản khác bạn nên học :-)


528

Từ Rick Strahl :

Bạn có thể xâu chuỗi ?? toán tử để bạn có thể thực hiện một loạt các so sánh null.

string result = value1 ?? value2 ?? value3 ?? String.Empty;

454

Chung chung bí danh:

using ASimpleName = Dictionary<string, Dictionary<string, List<string>>>;

Nó cho phép bạn sử dụng ASimpleName, thay vì Dictionary<string, Dictionary<string, List<string>>>.

Sử dụng nó khi bạn sẽ sử dụng cùng một thứ phức tạp lớn dài chung ở nhiều nơi.


438

Từ CLR qua C # :

Khi chuẩn hóa các chuỗi, chúng tôi khuyên bạn nên sử dụng ToUpperInvariant thay vì ToLowerInvariant vì Microsoft đã tối ưu hóa mã để thực hiện so sánh chữ hoa .

Tôi nhớ một lần đồng nghiệp của tôi luôn thay đổi chuỗi thành chữ hoa trước khi so sánh. Tôi đã luôn tự hỏi tại sao anh ấy làm điều đó bởi vì tôi cảm thấy nó "tự nhiên" hơn để chuyển đổi thành chữ thường trước. Sau khi đọc cuốn sách bây giờ tôi biết tại sao.


254
Khi bạn "chuyển đổi một chuỗi thành chữ hoa", bạn tạo một đối tượng chuỗi tạm thời thứ hai. Tôi nghĩ rằng loại so sánh này không được ưa thích, cách tốt nhất là: String.Equals (stringA, stringB, StringComparison.CienCARMIgnoreCase) whcih hoàn toàn không tạo ra chuỗi ném bỏ này.
Anthony

32
Loại tối ưu hóa nào bạn có thể thực hiện khi so sánh các chuỗi chữ hoa không thể được thực hiện trên các chuỗi chữ thường? Tôi không hiểu tại sao cái này lại tối ưu hơn cái kia.
Parappa

36
Chuyển đổi thành chữ hoa thay vì chữ thường cũng có thể ngăn chặn hành vi không chính xác trong các nền văn hóa nhất định. Ví dụ: trong tiếng Thổ Nhĩ Kỳ, hai bản đồ chữ thường i thành chữ hoa I. Google "thổ nhĩ kỳ" để biết thêm chi tiết.
Neil

34
Tôi đã thử điểm chuẩn ToUpperInvariant so với ToLowerInvariant. Tôi không thể tìm thấy bất kỳ sự khác biệt nào trong hiệu suất của chúng trong .NET 2.0 hoặc 3.5. Chắc chắn không có bất cứ điều gì đảm bảo "rất khuyến khích" sử dụng cái này hơn cái kia.
Rasmus Faber

19
ToUpperInvariant được ưa thích vì nó làm cho tất cả các nhân vật khứ hồi. Xem msdn.microsoft.com/en-us/l Library / bb386042.aspx . Để so sánh, hãy viết"a".Equals("A", StringComparison.OrdinalIgnoreCase)
SLaks

408

Thủ thuật yêu thích của tôi là sử dụng toán tử hợp nhất null và dấu ngoặc đơn để tự động khởi tạo các bộ sưu tập cho tôi.

private IList<Foo> _foo;

public IList<Foo> ListOfFoo 
    { get { return _foo ?? (_foo = new List<Foo>()); } }

23
Bạn không thấy khó đọc à?
Riri

72
Nó hơi khó đọc cho người mới ... er, thiếu kinh nghiệm. Nhưng nó nhỏ gọn và chứa một vài mẫu và tính năng ngôn ngữ mà lập trình viên nên biết và hiểu. Vì vậy, mặc dù khó khăn lúc đầu, nó mang lại lợi ích là lý do để học hỏi.

38
Khởi tạo lười biếng có phần không chính xác vì đó là một lựa chọn của người nghèo để tránh suy nghĩ về bất biến giai cấp. Nó cũng có vấn đề tương tranh.
John Leidegren

17
Tôi đã luôn được thông báo rằng đây là một thực tế tồi tệ, rằng việc gọi một tài sản không nên làm điều gì đó như vậy. Nếu bạn có một bộ trên đó và đặt giá trị thành null, sẽ rất lạ đối với ai đó sử dụng API của bạn.
Ian

8
@Joel ngoại trừ việc đặt ListOfFoo thành null không phải là một phần của hợp đồng lớp cũng không phải là thông lệ tốt. Đó là lý do tại sao không có setter. Đó cũng là lý do tại sao ListOfFoo được đảm bảo trả lại một bộ sưu tập và không bao giờ là null. Nó chỉ là một khiếu nại rất kỳ quặc rằng nếu bạn làm hai điều xấu (tạo một setter và đặt một bộ sưu tập thành null) thì điều này sẽ dẫn đến kết quả của bạn là sai. Tôi cũng sẽ không đề xuất làm Môi trường.Exit () trong getter. Nhưng điều đó cũng không có gì để làm với câu trả lời này.

315

Tránh kiểm tra xử lý sự kiện null

Thêm một đại biểu trống vào các sự kiện khi khai báo, không cần phải luôn luôn kiểm tra sự kiện đó trước khi gọi nó là tuyệt vời. Thí dụ:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click = delegate {}; // add empty delegate!

Để bạn làm điều này

public void DoSomething()
{
    Click(this, "foo");
}

Thay vì cái này

public void DoSomething()
{
    // Unnecessary!
    MyClickHandler click = Click;
    if (click != null) // Unnecessary! 
    {
        click(this, "foo");
    }
}

Xin vui lòng xem cuộc thảo luận liên quan này và bài đăng trên blog này của Eric Lippert về chủ đề này (và những nhược điểm có thể xảy ra).


87
Tôi tin rằng một vấn đề sẽ xuất hiện nếu bạn dựa vào kỹ thuật này và sau đó bạn phải tuần tự hóa lớp học. Bạn sẽ loại bỏ sự kiện này, và sau đó, quá trình khử lưu huỳnh, bạn sẽ nhận được NullRefference .... Vì vậy, người ta chỉ có thể bám vào "cách cũ" để làm việc. An toàn hơn.
sirrocco

16
bạn vẫn có thể đặt trình xử lý sự kiện của mình thành null, vì vậy bạn vẫn có thể nhận được tham chiếu null và bạn vẫn có điều kiện cuộc đua.
Robert Paulson

64
Kiểm tra hồ sơ nhanh cho thấy trình xử lý sự kiện được đăng ký giả mà không có kiểm tra null mất khoảng gấp đôi thời gian của trình xử lý sự kiện chưa được đăng ký với kiểm tra null. Trình xử lý sự kiện Multicast không có kiểm tra null mất khoảng 3,5 lần thời gian của trình xử lý sự kiện singlecast với kiểm tra null.
P Daddy

54
Điều này tránh sự cần thiết phải kiểm tra null bằng cách luôn luôn có một người đăng ký tự. Ngay cả khi một sự kiện trống rỗng, điều này mang một chi phí mà bạn không muốn. Nếu không có người đăng ký nào bạn không muốn tổ chức sự kiện này, không phải lúc nào cũng tổ chức một sự kiện giả trống. Tôi sẽ xem xét mã xấu này.
Keith

56
Đây là một gợi ý khủng khiếp, vì những lý do trong các ý kiến ​​trên. Nếu bạn phải làm cho mã của mình trông "sạch", hãy sử dụng phương thức mở rộng để kiểm tra null sau đó gọi sự kiện. Ai đó có đặc quyền sửa đổi chắc chắn nên thêm khuyết điểm cho câu trả lời này.
Greg

305

Mọi thứ khác, cộng với

1) khái quát chung (tại sao chỉ trên các phương thức mà không phải trên các lớp học?)

void GenericMethod<T>( T input ) { ... }

//Infer type, so
GenericMethod<int>(23); //You don't need the <>.
GenericMethod(23);      //Is enough.

2) lambdas đơn giản với một tham số:

x => x.ToString() //simplify so many calls

3) các loại ẩn danh và khởi tạo:

//Duck-typed: works with any .Add method.
var colours = new Dictionary<string, string> {
    { "red", "#ff0000" },
    { "green", "#00ff00" },
    { "blue", "#0000ff" }
};

int[] arrayOfInt = { 1, 2, 3, 4, 5 };

Một số khác:

4) Thuộc tính tự động có thể có phạm vi khác nhau:

public int MyId { get; private set; }

Cảm ơn @pzycoman đã nhắc nhở tôi:

5) Bí danh không gian tên (không phải là bạn có thể cần sự khác biệt đặc biệt này):

using web = System.Web.UI.WebControls;
using win = System.Windows.Forms;

web::Control aWebControl = new web::Control();
win::Control aFormControl = new win::Control();

14
trong # 3, bạn có thể thực hiện
En đếmable.Range

18
tôi nghĩ rằng bạn đã có thể khởi tạo mảng bằng int [] nums = {1,2,3}; kể từ 1.0 :) thậm chí không cần từ khóa "mới"
Lucas

13
còn lambda không có tham số () => DoS Something ();
Pablo Retyk

5
Tôi đã sử dụng cả {get; bộ nội bộ; } và lấy; bộ bảo vệ; }, vì vậy mô hình này là phù hợp.
Keith

7
@Kirk Broadhurst - bạn nói đúng - new web.Control()cũng sẽ hoạt động trong ví dụ này. Các ::lực lượng cú pháp nó để điều trị các tiền tố như một bí danh namespace, vì vậy bạn có thể có một lớp được gọi webweb::Controlcú pháp vẫn sẽ làm việc, trong khi web.Controlcú pháp sẽ không biết liệu để kiểm tra các lớp học hoặc không gian tên. Do đó, tôi có xu hướng luôn sử dụng ::khi thực hiện các bí danh không gian tên.
Keith

286

Tôi đã không biết từ khóa "như" trong một thời gian dài.

MyClass myObject = (MyClass) obj;

đấu với

MyClass myObject = obj as MyClass;

Cái thứ hai sẽ trả về null nếu obj không phải là MyClass, thay vì ném ngoại lệ cast class.


42
Đừng làm quá nhiều mặc dù. Nhiều người dường như sử dụng vì cú pháp thích mặc dù họ muốn ngữ nghĩa của một (ToType) x.
Scott Langham

4
Tôi không tin rằng nó cung cấp hiệu suất tốt hơn. Bạn đã định hình nó chưa? (Rõ ràng mặc dù nó khi các diễn viên thất bại ... nhưng khi bạn sử dụng (MyClass) đúc, thất bại là ngoại lệ .. và cực kỳ hiếm (nếu chúng xảy ra ở tất cả), vì vậy nó làm cho không có sự khác biệt.
Scott Langham

7
Điều này chỉ hiệu quả hơn nếu trường hợp thông thường là diễn viên thất bại. Nếu không, đối tượng cast trực tiếp (loại) nhanh hơn. Mặc dù phải mất nhiều thời gian hơn để một diễn viên trực tiếp ném ngoại lệ hơn là trả về null.
Spence

15
Ngay dọc theo cùng một dòng của từ khóa "như" ... từ khóa "là" cũng hữu ích.
dkpatt

28
Bạn có thể lạm dụng nó và có NullReferenceException sau khi bạn có thể có UnlimitedCastException trước đó.
Andrei Rînea

262

Hai điều tôi thích là thuộc tính Tự động để bạn có thể thu gọn mã của mình xuống hơn nữa:

private string _name;
public string Name
{
    get
    {
        return _name;
    }
    set
    {
        _name = value;
    }
}

trở thành

public string Name { get; set;}

Ngoài ra đối tượng khởi tạo:

Employee emp = new Employee();
emp.Name = "John Smith";
emp.StartDate = DateTime.Now();

trở thành

Employee emp = new Employee {Name="John Smith", StartDate=DateTime.Now()}

6
Cần lưu ý rằng Thuộc tính tự động là tính năng chỉ có C # 3.0?
Jared Updike

21
Automatic Properties được giới thiệu với trình biên dịch 3.0. Nhưng vì trình biên dịch có thể được đặt thành mã đầu ra 2.0, chúng chỉ hoạt động tốt. Đừng cố biên dịch mã 2.0 với các thuộc tính tự động trong trình biên dịch cũ hơn!
Joshua Shannon

74
Một cái gì đó mà nhiều người không nhận ra là get và set có thể có khả năng truy cập khác nhau, ví dụ: Tên chuỗi công khai {get; thiết lập riêng tư;}
Nader Shirazie

7
Chỉ có vấn đề với Thuộc tính tự động là dường như không thể cung cấp giá trị khởi tạo mặc định.
Stein smul

7
@ANeves: không, không phải vậy. Từ trang đó: DefaultValueAttribution sẽ không khiến thành viên được tự động khởi tạo với giá trị của thuộc tính. Bạn phải đặt giá trị ban đầu trong mã của bạn. [DefaultValue]được sử dụng cho người thiết kế để nó biết có hiển thị thuộc tính in đậm hay không (nghĩa là không mặc định).
Roger Lipscombe

255

Từ khóa 'mặc định' trong các loại chung:

T t = default(T);

kết quả là 'null' nếu T là loại tham chiếu và 0 nếu đó là int, false nếu đó là boolean, vân vân.


4
Cộng: loại? làm lối tắt cho Nullable <type>. mặc định (int) == 0, nhưng mặc định (int?) == null.
trời


221

@ Nói trình biên dịch bỏ qua mọi ký tự thoát trong chuỗi.

Chỉ muốn làm rõ cái này ... nó không bảo nó bỏ qua các ký tự thoát, nó thực sự bảo trình biên dịch diễn giải chuỗi dưới dạng một nghĩa đen.

Nếu bạn có

string s = @"cat
             dog
             fish"

nó thực sự sẽ in ra (lưu ý rằng nó thậm chí bao gồm khoảng trắng được sử dụng để thụt lề):

cat
             dog
             fish

chuỗi có bao gồm tất cả các khoảng trống mà bạn đã sử dụng để thụt lề không?
andy

18
Có, nó được gọi là chuỗi nguyên văn.
Joan Venge

4
Sẽ rõ ràng hơn nếu đầu ra cũng hiển thị các khoảng trắng sẽ được in ra. Ngay bây giờ có vẻ như các ký tự dòng mới được in nhưng khoảng trắng bị bỏ qua.
aleemb

Rất hữu ích để thoát các biểu thức thông thường và các truy vấn SQL dài.
tro999

Nó cũng có bản đồ {{để {}}để }làm cho nó hữu ích cho string.Format().
Ferruccio

220

Tôi nghĩ rằng một trong những tính năng được đánh giá thấp và ít được biết đến nhất của C # (.NET 3.5) là Cây biểu hiện , đặc biệt là khi kết hợp với Generics và Lambdas. Đây là một cách tiếp cận để tạo API mà các thư viện mới hơn như NInject và Moq đang sử dụng.

Ví dụ: giả sử tôi muốn đăng ký một phương thức với API và API đó cần có được tên phương thức

Cho lớp này:

public class MyClass
{
     public void SomeMethod() { /* Do Something */ }
}

Trước đây, rất phổ biến khi thấy các nhà phát triển làm điều này với các chuỗi và loại (hoặc một cái gì đó chủ yếu dựa trên chuỗi):

RegisterMethod(typeof(MyClass), "SomeMethod");

Vâng, đó là hút vì thiếu gõ mạnh. Nếu tôi đổi tên "someMethod" thì sao? Bây giờ, trong 3.5, tuy nhiên, tôi có thể thực hiện việc này theo kiểu được gõ mạnh:

RegisterMethod<MyClass>(cl => cl.SomeMethod());

Trong đó lớp RegisterMethod sử dụng Expression<Action<T>>như thế này:

void RegisterMethod<T>(Expression<Action<T>> action) where T : class
{
    var expression = (action.Body as MethodCallExpression);

    if (expression != null)
    {
        // TODO: Register method
        Console.WriteLine(expression.Method.Name);
    }
}

Đây là một lý do lớn khiến tôi yêu Lambdas và Expression Tree ngay bây giờ.


3
Tôi có một lớp tiện ích phản chiếu tương tự với FieldInfo, PropertyInfo, v.v ...
Olmo

Vâng, điều này thật tuyệt. Tôi đã có thể sử dụng các phương thức như thế này để viết mã như EditValue(someEmployee, e => e.FirstName);trong logic nghiệp vụ của mình và để nó tự động tạo ra tất cả logic hệ thống ống nước cho ViewModel và View để chỉnh sửa thuộc tính đó (vì vậy, một nhãn có chữ "Tên" và TextBox với một ràng buộc gọi bộ setter của thuộc tính FirstName khi người dùng chỉnh sửa tên và cập nhật View bằng cách sử dụng getter). Đây dường như là cơ sở cho hầu hết các DSL nội bộ mới trong C #.
Scott Whitlock

4
Tôi nghĩ những điều này không quá ít được biết đến như là ít hiểu biết.
Justin Morgan

Tại sao bạn cần phải đăng ký một phương pháp? Tôi chưa bao giờ sử dụng cái này trước đây - nó sẽ được sử dụng ở đâu và khi nào?
MoonKnight

209

" năng suất " sẽ đến với tâm trí của tôi. Một số thuộc tính như [DefaultValue ()] cũng nằm trong số các mục yêu thích của tôi.

Các " var " từ khóa là nổi tiếng hơn một chút, nhưng mà bạn có thể sử dụng nó trong các ứng dụng .NET 2.0 cũng như (miễn là bạn sử dụng trình biên dịch .NET 3.5 và đặt nó là mã đầu ra 2.0) dường như không được biết đến rất tốt.

Chỉnh sửa: kokos, cảm ơn vì đã chỉ ra ?? Toán tử, điều đó thực sự hữu ích. Vì hơi khó để google cho nó (vì ?? chỉ bị bỏ qua), đây là trang tài liệu MSDN cho nhà điều hành đó: ?? Toán tử (Tham khảo C #)


14
Tài liệu của giá trị mặc định nói rằng nó không thực sự thiết lập giá trị của thuộc tính. Nó chỉ là một trợ giúp cho trình hiển thị và trình tạo mã.
Boris Callens

2
Đối với DefaultValue: Trong khi đó, một số thư viện sử dụng nó. ASP.net MVC sử dụng DefaultValue trên các tham số của hành động điều khiển (Điều này rất hữu ích cho các loại không nullable). Tất nhiên, nói đúng ra, đây là một trình tạo mã vì giá trị không được đặt bởi trình biên dịch mà bởi mã của MVC.
Michael Stum

6
Tên cho ?? toán tử là toán tử "Null Coalescing"
Armstrongest

năng suất là yêu thích của tôi, mặc dù các toán tử hợp nhất là ở đó. Tôi không thấy bất cứ điều gì về CAS, hoặc ký kết lắp ráp, tên mạnh, GAC ... Tôi cho rằng chỉ CAS là C # ... nhưng rất nhiều nhà phát triển không có manh mối về bảo mật.
BrainSlugs83

198

Tôi có xu hướng thấy rằng hầu hết các nhà phát triển C # không biết về các loại 'nullable'. Về cơ bản, nguyên thủy có thể có giá trị null.

double? num1 = null; 
double num2 = num1 ?? -100;

Đặt một đôi không thể, num1 , thành null, sau đó đặt một đôi thông thường, num2 , thành num1 hoặc -100 nếu num1 là null.

http://msdn.microsoft.com/en-us/l Library / 1t3y8s4s (VS.80) .aspx

một điều nữa về loại Nullable:

DateTime? tmp = new DateTime();
tmp = null;
return tmp.ToString();

đó là trả về String.Empty. Kiểm tra liên kết này để biết thêm chi tiết


1
DateTime không thể được đặt thành null.
Jason Jackson

2
Vậy thì "int" chỉ là đường cú pháp C # cho System.Int32? Thực tế, có hỗ trợ trình biên dịch được xây dựng xung quanh các loại Nullable, ví dụ, cho phép đặt chúng thành null (không thể thực hiện bằng cách sử dụng các cấu trúc chung) và đóng hộp chúng làm loại cơ bản.
P Daddy

4
@P Daddy - vâng, int là cú pháp cú pháp cho System.Int32. Chúng hoàn toàn có thể hoán đổi cho nhau, giống như int? === Int32? === Không thể <Int32> === Không thể <int>
cjk

6
@ck: Có, int là bí danh cho System.Int32, là T? là một bí danh cho Nullable <T>, nhưng nó không chỉ là đường cú pháp. Đó là một phần xác định của ngôn ngữ. Các bí danh không chỉ làm cho mã dễ đọc hơn mà còn phù hợp với mô hình của chúng tôi hơn. Thể hiện Nullable <Double> là gấp đôi? nhấn mạnh viễn cảnh rằng giá trị chứa trong đó là (hoặc có thể) là gấp đôi, không chỉ là một số cấu trúc chung được khởi tạo trên loại Double. (còn tiếp)
P Daddy

3
... Trong mọi trường hợp, đối số không phải là về bí danh (đó chỉ là một sự tương tự kém, tôi cho rằng), nhưng loại nullable đó sử dụng bất kỳ cú pháp nào bạn thích, thực sự là một tính năng ngôn ngữ, không chỉ là một sản phẩm của khái quát. Bạn không thể tái tạo tất cả các chức năng của nullables (so sánh với / gán null, chuyển tiếp toán tử, quyền anh thuộc loại cơ bản hoặc null, tương thích với phép kết hợp null và astoán tử) chỉ với các tổng quát. Nullable <T> một mình là thô sơ và xa sao, nhưng khái niệm về các loại nullable như một phần của ngôn ngữ là kick ass.
P Daddy

193

Dưới đây là một số tính năng C # ẩn thú vị, dưới dạng từ khóa C # không có giấy tờ:

__makeref

__reftype

__refvalue

__arglist

Đây là những từ khóa C # không có giấy tờ (ngay cả Visual Studio cũng nhận ra chúng!) Đã được thêm vào để đấm bốc / bỏ hộp hiệu quả hơn trước khi sử dụng thuốc generic. Chúng phối hợp với cấu trúc System.TypedReference.

Ngoài ra còn có __arglist, được sử dụng cho danh sách tham số độ dài thay đổi.

Một điều mọi người không biết nhiều là System.WeakReference - một lớp rất hữu ích để theo dõi một đối tượng nhưng vẫn cho phép người thu gom rác thu thập nó.

Tính năng "ẩn" hữu ích nhất sẽ là từ khóa lợi nhuận. Nó không thực sự bị ẩn, nhưng rất nhiều người không biết về nó. LINQ được xây dựng trên đỉnh này; nó cho phép các truy vấn được thực hiện trễ bằng cách tạo một máy trạng thái dưới mui xe. Raymond Chen gần đây đã đăng về các chi tiết nội bộ, nghiệt ngã .


2
Thêm chi tiết về các từ khóa không có giấy tờ của Peter Bromberg . Tôi vẫn không nhận được nếu có lý do để sử dụng chúng.
HuBeZa

@HuBeZa với sự ra đời của thuốc generic, không có nhiều lý do chính đáng để sử dụng __VfType, __makeref và __Vfvalue. Chúng được sử dụng chủ yếu để tránh quyền anh trước khi có tướng trong .NET 2.
Judah Gabriel Himango

Matt, với việc giới thiệu thuốc generic trong .NET 2, có rất ít lý do để sử dụng các từ khóa này, vì mục đích của chúng là để tránh quyền anh khi xử lý các loại giá trị. Xem liên kết của HuBeZa và cũng xem codeproject.com/Articles/38695/UnCommon-C-keywords-A-Look#ud
Judah Gabriel Himango

185

Liên hiệp (loại bộ nhớ chia sẻ C ++) trong C # thuần túy, an toàn

Không cần dùng đến chế độ và con trỏ không an toàn, bạn có thể yêu cầu các thành viên lớp chia sẻ không gian bộ nhớ trong một lớp / struct. Cho lớp sau:

[StructLayout(LayoutKind.Explicit)]
public class A
{
    [FieldOffset(0)]
    public byte One;

    [FieldOffset(1)]
    public byte Two;

    [FieldOffset(2)]
    public byte Three;

    [FieldOffset(3)]
    public byte Four;

    [FieldOffset(0)]
    public int Int32;
}

Bạn có thể sửa đổi các giá trị của các trường byte bằng cách thao tác trường Int32 và ngược lại. Ví dụ, chương trình này:

    static void Main(string[] args)
    {
        A a = new A { Int32 = int.MaxValue };

        Console.WriteLine(a.Int32);
        Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four);

        a.Four = 0;
        a.Three = 0;
        Console.WriteLine(a.Int32);
    }

Đầu ra này:

2147483647
FF FF FF 7F
65535

chỉ cần thêm bằng System.R.78.InteropService;


7
@George, hoạt động kỳ diệu khi bạn giao tiếp với các ứng dụng cũ qua ổ cắm bằng cách sử dụng khai báo c union.
scottm

2
Cũng có ý nghĩa để nói int và float ở offset 0. Đó là những gì bạn cần nếu bạn muốn thao tác các số dấu phẩy động như mặt nạ bit, đôi khi bạn muốn. Đặc biệt nếu bạn muốn tìm hiểu những điều mới về số dấu phẩy động.
John Leidegren

2
Điều khó chịu về điều này là nếu bạn sẽ sử dụng đây là một cấu trúc trình biên dịch sẽ buộc bạn phải đặt TẤT CẢ các biến trong hàm init. Vì vậy, nếu bạn có: công khai A (int int32) {Int32 = int32; } nó sẽ ném "Trường 'Một' phải được gán đầy đủ trước khi trả lại quyền điều khiển cho người gọi", vì vậy bạn phải đặt One = Two = Three = Four = 0; cũng.
manixrock

2
Điều này có công dụng của nó, chủ yếu là với dữ liệu nhị phân. Tôi sử dụng cấu trúc "Pixel" với int32 @ 0 và bốn byte cho bốn thành phần @ 0, 1, 2 và 3. Tuyệt vời để thao tác dữ liệu hình ảnh nhanh chóng và dễ dàng.
snarf

57
Cảnh báo: Cách tiếp cận này không tính đến tuổi thọ. Điều đó có nghĩa là mã C # của bạn sẽ không chạy cùng một cách trên tất cả các máy. Trên các CPU nhỏ (lưu trữ byte đầu tiên ít quan trọng nhất), hành vi được hiển thị sẽ được sử dụng. Nhưng trên các CPU lớn, các byte sẽ bị đảo ngược so với những gì bạn mong đợi. Coi chừng cách bạn sử dụng mã này trong mã sản xuất - mã của bạn có thể không di động được với một số thiết bị di động và phần cứng khác và có thể bị hỏng theo những cách không rõ ràng (ví dụ: hai tệp có vẻ giống nhau nhưng thực sự bị đảo ngược thứ tự byte).
Ray đốt cháy

176

Sử dụng @ cho tên biến là từ khóa.

var @object = new object();
var @string = "";
var @if = IpsoFacto(); 

38
Tại sao bạn muốn sử dụng một từ khóa làm tên biến? Dường như với tôi rằng điều này sẽ làm cho mã ít đọc và bị xáo trộn hơn.
Jon

41
Chà, lý do là CLI yêu cầu nó có khả năng tương tác với các ngôn ngữ khác có thể sử dụng từ khóa C # làm tên thành viên
Mark Cidade

69
Nếu bạn đã từng muốn sử dụng trình trợ giúp HTML MVC của asp.net và xác định một lớp HTML, bạn sẽ rất vui khi biết rằng bạn có thể sử dụng @ class để nó không được công nhận là từ khóa của lớp
Boris Callens

31
Tuyệt vời cho các phương pháp mở rộng. public static void DoS Something (this Bar @this, string foo) {...}
Jonathan C Dickinson

45
@zihotki: Sai rồi. var a = 5; Bảng điều khiển.WriteLine (@a); In 5
SLaks

168

Nếu bạn muốn thoát khỏi chương trình của mình mà không gọi bất kỳ khối cuối cùng hoặc bộ hoàn thiện nào, hãy sử dụng FailFast :

Environment.FailFast()

12
Lưu ý rằng phương pháp này cũng tạo kết xuất bộ nhớ và ghi thông báo vào nhật ký lỗi Windows.
RickNZ

1
Cần lưu ý rằng không có sự kiện nào trong
quá trình giải nén

1
+1 cho gợi ý, nhưng đây không phải là C #, đó là BCL của .NET.
Abel

System.Diagnostics.Process.GetCienProcess (). Kill () nhanh hơn
Tolgahan Albayrak

153

Trả về các loại ẩn danh từ một phương thức và truy cập các thành viên mà không cần phản ánh.

// Useful? probably not.
private void foo()
{
    var user = AnonCast(GetUserTuple(), new { Name = default(string), Badges = default(int) });
    Console.WriteLine("Name: {0} Badges: {1}", user.Name, user.Badges);
}

object GetUserTuple()
{
    return new { Name = "dp", Badges = 5 };
}    

// Using the magic of Type Inference...
static T AnonCast<T>(object obj, T t)
{
   return (T) obj;
}

42
Điều đó thực sự không giúp bạn có được bất cứ điều gì. Nó thực sự nguy hiểm. Điều gì xảy ra nếu GetUserTuple được sửa đổi để trả về nhiều loại? Các diễn viên sẽ thất bại trong thời gian chạy. Một trong những điều tuyệt vời về C # /. Net là kiểm tra thời gian biên dịch. Sẽ tốt hơn nhiều nếu chỉ tạo một loại mới.
Jason Jackson

9
@Jason Tôi đã nói nó có thể không hữu ích nhưng nó đáng ngạc nhiên (và tôi nghĩ ẩn).
denis phillips

31
Trong khi mát mẻ, điều này có vẻ như một sự lựa chọn thiết kế khá kém. Về cơ bản, bạn đã xác định loại ẩn danh ở hai nơi. Tại thời điểm đó, chỉ cần khai báo một cấu trúc thực và sử dụng nó trực tiếp.
Paul Alexander

6
@George: một quy ước như vậy sẽ được gọi là ... struct?
R. Martinho Fernandes

2
thủ thuật này được đặt tên là 'cast by sample' và nó sẽ không hoạt động nếu phương thức trả về kiểu ẩn danh nằm trong một hội đồng khác.
desco

146

Đây là một cách hữu ích cho các biểu thức và đường dẫn tệp thông thường:

"c:\\program files\\oldway"
@"c:\program file\newway"

@ Nói trình biên dịch bỏ qua mọi ký tự thoát trong chuỗi.


27
Ngoài ra, một hằng số @ chấp nhận các dòng mới bên trong. Hoàn hảo khi gán một tập lệnh multiline cho một chuỗi.
Tor Haugen

11
Nói cách khác, đừng quên thoát dấu ngoặc kép, nói cách khác. [code] var candy = @ "Tôi thích kẹo" "đỏ" "."; [/ code]
Dave

5
Tôi có xu hướng xây dựng các đường dẫn với Path.Combine. Tôi chắc chắn sử dụng @ cho regex!
Dan McClain

6
@new cũng là một biến thay vì một từ khóa: @this, @int, @return, @interface ... vân vân :)
Tôi chấp nhận

Điều này không đến bất cứ nơi nào gần: Nhưng các tính năng hoặc thủ thuật ẩn nhất của C # mà ngay cả người hâm mộ, người nghiện, chuyên gia C # hầu như không biết là gì?
khai

141

Hỗn hợp. Về cơ bản, nếu bạn muốn thêm một tính năng cho một số lớp, nhưng không thể sử dụng một lớp cơ sở cho tất cả chúng, hãy yêu cầu mỗi lớp thực hiện một giao diện (không có thành viên). Sau đó, viết một phương thức mở rộng cho giao diện , tức là

public static DeepCopy(this IPrototype p) { ... }

Tất nhiên, một số sự rõ ràng là hy sinh. Nhưng nó đã có tác dụng!


4
Vâng tôi nghĩ rằng đây là sức mạnh thực sự của các phương pháp mở rộng. Về cơ bản chúng cho phép thực hiện các phương thức giao diện.
John Bubriski

Điều này cũng thuận tiện nếu bạn đang sử dụng NHibernate (hoặc Castle ActiveRecord) và bạn phải sử dụng giao diện cho các bộ sưu tập của mình. Bằng cách này bạn có thể đưa ra hành vi cho các giao diện bộ sưu tập.
Ryan Lundy

Về cơ bản, đó là cách tất cả các phương pháp LINQ được thực hiện, cho hồ sơ.
WCWedin

Đây là một liên kết nói về những gì tôi đã đề cập ở trên, sử dụng các phương thức mở rộng với giao diện bộ sưu tập trong NHibernate hoặc Castle ActiveRecord: devlicio.us/bloss/billy_mccafferty/archive/2008/09/03/ Khăn
Ryan Lundy

7
Nếu chỉ họ cho phép mở rộng tài sản !!!! Tôi ghét phải viết một phương thức mở rộng mà xin được trở thành một tài sản chỉ đọc ....
John Gibb

130

Không chắc chắn tại sao mọi người sẽ muốn sử dụng Nullable <bool> mặc dù. :-)

Đúng, Sai, FileNotFound ?


87
nếu mong muốn người dùng trả lời có, không có câu hỏi thì null sẽ phù hợp nếu câu hỏi chưa được trả lời
Omar Kooheji

22
Các loại Nullable rất tiện cho việc tương tác với cơ sở dữ liệu trong đó các cột trong bảng thường không có giá trị.
tuinstoel

11
Có, không, Maybee?
Dan Blair

21
Lưu trữ các giá trị của Hộp kiểm tra ThreeState
Shimmy Weitzhandler

8
Như trong SQL: Có / không / không biết.
erikkallen

116

Cái này không "ẩn" quá nhiều vì nó được đặt tên sai.

Rất nhiều sự chú ý được dành cho các thuật toán "bản đồ", "thu nhỏ" và "bộ lọc". Điều mà hầu hết mọi người không nhận ra là .NET 3.5 đã thêm cả ba thuật toán này, nhưng nó mang lại cho họ những cái tên rất SQL, dựa trên thực tế rằng chúng là một phần của LINQ.

"bản đồ" => Chọn
Chuyển đổi dữ liệu từ dạng này sang dạng khác

"giảm" => Tổng hợp
hợp các giá trị Tổng hợp thành một kết quả duy nhất

"bộ lọc" => Nơi
bộ lọc dữ liệu dựa trên một tiêu chí

Khả năng sử dụng LINQ để thực hiện công việc nội tuyến trên các bộ sưu tập được sử dụng để lặp lại và điều kiện có thể cực kỳ có giá trị. Thật đáng để tìm hiểu làm thế nào tất cả các phương thức mở rộng LINQ có thể giúp làm cho mã của bạn gọn hơn và dễ bảo trì hơn nhiều.


1
Chọn cũng hoạt động như chức năng "trở lại" trong các đơn nguyên. Xem stackoverflow.com/questions/9033/hidden-features-of-c#405088
Đánh dấu Cidade

1
Sử dụng "select" chỉ được yêu cầu nếu bạn sử dụng cú pháp SQLish. Nếu bạn sử dụng cú pháp phương thức mở rộng - someCollection.Where (item => item.price> 5,99M) - việc sử dụng các câu lệnh chọn không bắt buộc.
Brad Wilson

7
@Brad, đó (nơi) là một hoạt động lọc. Hãy thử thực hiện thao tác trên bản đồ mà không chọn ...
Eloff

2
LINQ là điều lớn đã xảy ra với C # theo ý kiến ​​của tôi: stackoverflow.com/questions/2398818/
Đổi

1
Chữ ký nhỏ nhất của Uẩn là hàm "rút gọn", Chữ ký giữa của Uẩn là hàm "gấp" mạnh hơn nhiều!
Brett Widmeier

115
Environment.NewLine

cho các dòng mới độc lập hệ thống.


10
Điều khó chịu về điều này, là nó không được đưa vào khung nhỏ gọn.
Stormenet

14
Giá trị của nó chỉ ra rằng điều này là dành riêng cho nền tảng máy chủ của ứng dụng - vì vậy nếu bạn đang tạo dữ liệu dành cho hệ thống khác, bạn nên sử dụng \ n hoặc \ r \ n một cách thích hợp.
Lưới

Đây là một phần của BCL của .NET, không phải là một tính năng của C # per se.
Abel

111

Nếu bạn đang cố gắng sử dụng dấu ngoặc nhọn bên trong biểu thức String.Format ...

int foo = 3;
string bar = "blind mice";
String.Format("{{I am in brackets!}} {0} {1}", foo, bar);
//Outputs "{I am in brackets!} 3 blind mice"

19
@Kyralessa: Thật ra, vâng, chúng là niềng răng, nhưng "dấu ngoặc nhọn" là một tên thay thế cho chúng. []là dấu ngoặc vuông, <>là dấu ngoặc góc. Xem en.wikipedia.org/wiki/Brquet .
icktoofay

4
Bạn nói đúng. Nhưng khi tôi nhận xét, nó không nói dấu ngoặc "xoăn".
Ryan Lundy

2
Rất đẹp để chỉ ra. Hãy nhớ String.Format đầu tiên của tôi trông giống như: String.Format ("{0} Tôi đang ở trong dấu ngoặc nhọn {1} {2} {3}", "{", "}", 3, "chuột mù"); Khi tôi phát hiện ra rằng việc thoát những thứ này được thực hiện bằng cách sử dụng {{và}}, tôi đã rất hạnh phúc :)
Gertjan

Muahahahaa ... Lập trình đã 3 năm .Net và tôi không biết cái này. : D
Arnis Lapsa

dấu ngoặc "hành động" tương tự như chuỗi thoát ??
Pratik

104
  1. ?? - toán tử liên kết
  2. bằng cách sử dụng ( tuyên bố / chỉ thị ) - từ khóa tuyệt vời có thể được sử dụng nhiều hơn là chỉ gọi Vứt bỏ
  3. chỉ đọc - nên được sử dụng nhiều hơn
  4. netmodules - quá tệ không có hỗ trợ trong Visual Studio

6
việc sử dụng cũng có thể được sử dụng để đặt bí danh một không gian tên dài thành một chuỗi thuận tiện hơn, nghĩa là: sử dụng ZipEncode = MyCompany.UtilityCode.Compression.Zip.Encoding; Có nhiều hơn ở đây: msdn.microsoft.com/en-us/l Library / sf0df423.aspx
Dave R.

2
Nó thực sự không giống nhau. Khi gọi Vứt bỏ, bạn có thể sử dụng câu lệnh sử dụng, khi đặt bí danh các loại bạn đang sử dụng một lệnh sử dụng.
Øyvind Skaar

2
Chỉ trong trường hợp bạn muốn đặt tên cho # 1 (như bạn đã làm với toán tử ternary), ?? được gọi là toán tử hợp nhất null.
J. Steen

4
@LucasAardvark: Như J Steen đã đề cập, nó được gọi là toán tử liên kết null. Tìm kiếm cho điều đó!
kokos

1
Tìm kiếm cho ?? Nhà điều hành tại google thử: google.com/search?q=c%23+%3F%3F+operator
backslash17

103

@Ed, tôi hơi thận trọng về việc đăng bài này vì nó ít hơn nhiều so với nitpicking. Tuy nhiên, tôi sẽ chỉ ra rằng trong mẫu mã của bạn:

MyClass c;
  if (obj is MyClass)
    c = obj as MyClass

Nếu bạn sẽ sử dụng 'là', tại sao lại theo dõi nó với một dàn diễn viên an toàn bằng cách sử dụng 'as'? Nếu bạn đã xác định rằng obj thực sự là MyClass, một diễn viên tiêu chuẩn không có thật:

c = (MyClass)obj

... sẽ không bao giờ thất bại.

Tương tự, bạn chỉ có thể nói:

MyClass c = obj as MyClass;
if(c != null)
{
   ...
}

Tôi không biết đủ về các bộ phận của .NET để chắc chắn, nhưng bản năng của tôi nói với tôi rằng điều này sẽ cắt giảm tối đa hai loại hoạt động phôi xuống tối đa một. Dù sao thì nó cũng khó có thể phá vỡ ngân hàng xử lý; cá nhân, tôi nghĩ rằng hình thức sau có vẻ sạch sẽ hơn.


16
Nếu diễn viên thuộc loại chính xác (chuyển thành "A" khi đối tượng là "A", không xuất phát từ nó), thì diễn viên thẳng là ~ 3x FASTER so với "as". Khi truyền một loại dẫn xuất (chuyển thành "A" khi đối tượng là "B", xuất phát từ "A"), vật đúc thẳng chậm hơn ~ 0,1 lần so với "as". "là", sau đó "như" chỉ là ngớ ngẩn.
P Daddy

2
Không, nhưng bạn có thể viết "if ((c = obj as MyClass)! = Null)".
Dave Van den Eynde

10
isassẽ không làm diễn viên người dùng. Vì vậy, đoạn mã trên đang hỏi người isvận hành nếu obj có nguồn gốc từ MyClass (hoặc có một hệ thống ẩn được định nghĩa là cast). Ngoài ra, isthất bại trên null. Cả hai trường hợp cạnh này có thể quan trọng đối với mã của bạn. Chẳng hạn, bạn có thể muốn viết: if( obj == null || obj is MyClass ) c = (MyClass)obj; Nhưng điều này hoàn toàn khác với: try { c = (MyClass)obj; } catch { }vì cái trước sẽ không thực hiện bất kỳ chuyển đổi nào do người dùng xác định, nhưng cái sau sẽ. Nếu không có sự nullkiểm tra, cựu cũng sẽ không được thiết lập ckhi objnull.
Adam Luter

2
Trong IL, một diễn viên đi đến CASTCLASS nhưng as / được chuyển đến một lệnh ISINST.
John Gibb

5
Tôi đã chạy thử nghiệm này, truyền IEnumerable<int>tới List<int>và truyền object( new object()) tới IEnumerable<int>, để đảm bảo không có lỗi nào: cast trực tiếp: 5,43ns, là-> như cast: 6,75ns, như cast: 5,69ns. Sau đó kiểm tra phôi không hợp lệ: cast trực tiếp: 3125000ns, như cast: 5,41ns. Kết luận: hãy ngừng lo lắng về yếu tố 1% và chỉ cần đảm bảo rằng bạn sử dụng là / vì khi diễn viên có thể không hợp lệ, vì các trường hợp ngoại lệ (cũng nếu được xử lý) RẤT chậm so với truyền, chúng ta đang nói về yếu tố 578000 chậm hơn. Hãy nhớ rằng phần cuối cùng, phần còn lại không thành vấn đề (.Net FW 4.0, bản phát hành)
Aidiakapi

98

Có thể không phải là một kỹ thuật tiên tiến, nhưng một trong những điều tôi thấy suốt thời gian khiến tôi phát điên:

if (x == 1)
{
   x = 2;
}
else
{
   x = 3;
}

có thể được ngưng tụ để:

x = (x==1) ? 2 : 3;

10
Các nhà điều hành ternary đã bị cấm trong nhóm của tôi. Một số người không thích nó, mặc dù tôi chưa bao giờ hiểu tại sao.
Justin R.

13
Tôi đoán bởi vì chúng không giống nhau, là một nhà điều hành không phải là một tuyên bố. Tôi thích cách tiếp cận đầu tiên bản thân mình. Nó phù hợp hơn và dễ dàng mở rộng. Các toán tử không thể chứa các câu lệnh, vì vậy ngay khi bạn phải mở rộng cơ thể, bạn sẽ phải chuyển đổi toán tử tạm thời đó thành một câu lệnh if
kervin

16
có thể được ngưng tụ thành x ++; Ok thật vô nghĩa ^^
François

5
@Guillaume: Để tính tất cả các giá trị của x: x = 2 + System.Math.Min (1, System.Math.Abs ​​(x-1));
mbeckish

38
Nó thực sự được gọi là "nhà điều hành có điều kiện" - đó là một nhà điều hành ternary vì phải mất ba đối số. vi.wikipedia.org/wiki/Cond điều_operator
Blorgbeard sẽ ra mắt vào
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.