للمبتدئين - مشاريع جاهزة بلغة c#




استخدام الكلمة الرئيسية var في C# (20)

بعد مناقشة مع الزملاء فيما يتعلق باستخدام الكلمة الرئيسية "var" في C # 3 ، تساءلت ما هي آراء الناس حول الاستخدام المناسب للاستدلال النوعي عبر var؟

على سبيل المثال ، أنا استعملت على نحو فوضوي في ظروف مشكوك فيها ، على سبيل المثال: -

foreach(var item in someList) { // ... } // Type of 'item' not clear.
var something = someObject.SomeProperty; // Type of 'something' not clear.
var something = someMethod(); // Type of 'something' not clear.

الاستخدامات الأكثر شرعية لـ var هي كالتالي:

var l = new List<string>(); // Obvious what l will be.
var s = new SomeClass(); // Obvious what s will be.

من المثير للاهتمام أن LINQ يبدو قليلاً من مساحة رمادية ، على سبيل المثال: -

var results = from r in dataContext.SomeTable
              select r; // Not *entirely clear* what results will be here.

من الواضح ما ستكون النتائج في أنه سيكون من النوع الذي ينفذ IEnumerable ، ومع ذلك فإنه ليس من الواضح تماما بنفس الطريقة التي يعلن فيها عن وجود كائن جديد.

إنه أسوأ حتى عندما يتعلق الأمر LINQ إلى الأشياء ، على سبيل المثال: -

var results = from item in someList
              where item != 3
              select item;

هذا ليس أفضل من equeachent foreach (var item in someList) {// ...} equivilent.

هناك قلق حقيقي حول سلامة النوع هنا - على سبيل المثال ، إذا كنا سنضع نتائج هذا الاستعلام في طريقة فوق طاقتها تقبل IEnumerable <int> و IEnumerable <double> قد يمرر المتصل دون قصد في النوع الخاطئ.

لا يحتفظ var بكتابة قوية ولكن السؤال هو في الحقيقة ما إذا كان من الخطير أن لا يكون هذا النوع ظاهرًا على الفور في التعريف ، وهو أمر يتم تضخيمه عندما تعني الحمولة الزائدة أن أخطاء المترجم قد لا يتم إصدارها عند تمرير نوع الخطأ بطريقة غير مقصودة إلى طريقة ما.


أعتقد أن استخدام var يجب أن يقترن بأسماء المتغيرات المختارة بحكمة.

ليس لدي أي مشكلة في استخدام var في عبارة foreach ، بشرط ألا يكون هذا كما يلي:

foreach (var c in list) { ... }

لو كان أكثر مثل هذا:

foreach (var customer in list) { ... }

... سيكون من المحتمل جدًا أن يفهم شخص ما قراءة الشفرة ما هي "القائمة". إذا كنت تتحكم في اسم متغير القائمة نفسه ، فهذا أفضل.

ويمكن أن ينطبق الشيء نفسه على حالات أخرى. هذا غير مفيد

var x = SaveFoo(foo);

... لكن هذا منطقي:

var saveSucceeded = SaveFoo(foo);

كل واحد إلى بلده ، على ما أعتقد. لقد وجدت نفسي أفعل هذا ، وهو مجنون فقط:

var f = (float)3;

أحتاج إلى نوع من برنامج var-12-step. اسمي مات ، وأنا (أ ب) استخدام فار.


أنا استخدم var نطاق واسع. كان هناك انتقادات بأن هذا يقلل من إمكانية قراءة المدونة ، ولكن لا توجد حجة لدعم هذا الادعاء.

باعتراف الجميع ، قد يعني ذلك أنه ليس من الواضح ما هو النوع الذي نتعامل معه. وماذا في ذلك؟ هذا هو في الواقع نقطة التصميم المنفصل. عند التعامل مع الواجهات ، فأنت لا تهتم بشكل قاطع بالنوع الذي يحتويه المتغير. يأخذ var أكثر من ذلك بكثير ، صحيح ، لكني أعتقد أن الحجة تبقى كما هي من وجهة نظر قابلية القراءة: لا ينبغي للمبرمج أن يكون مهتمًا بالفعل بنوع المتغير ، بل بالأحرى ما يفعله المتغير. ولهذا السبب تقوم Microsoft أيضًا بالاتصال باستدعاء الكتابة "كتابة بطة".

لذا ، ماذا يفعل المتغير عندما أعلن عنه باستخدام var ؟ سهل ، يفعل أي شيء يخبرني IntelliSense أنه يفعل. أي منطق عن C # يتجاهل IDE يقصر عن الواقع. في الممارسة ، يتم برمجة كل رمز C # في IDE يعتمد IntelliSense.

إذا كنت أستخدم متغير var وأحصل على الخلط بين المتغير الموجود هناك ، فهناك شيء خاطئ جوهري في شفرتي. var ليس السبب ، فهو يجعل الأعراض مرئية فقط. لا ألوم الرسول

الآن ، أصدر فريق C # توجيهًا ترميزًا ينص على أنه يجب استخدام var فقط للحصول على نتيجة عبارة LINQ التي تنشئ نوعًا مجهولاً (لأن هنا ، ليس لدينا بديل حقيقي لـ var ). حسنا ، هذا المسمار. طالما أن فريق C # لا يعطيني حجة سليمة لهذا المبدأ التوجيهي ، فسوف أتجاهله لأنه برأيي المهني والشخصي ، إنه هراء خالص. (عذرا ؛ ليس لدي أي رابط للمبدأ التوجيهي المعني.)

في الواقع ، هناك بعض التفسيرات الجيدة (السطحية) حول سبب عدم استخدام var لكن ما زلت أعتقد أنها خاطئة إلى حد كبير. لنأخذ مثال "البحث": يدعي المؤلف أن var يصعّب البحث عن أماكن تستخدم فيها MyType . حق. تفعل ذلك واجهات. في الواقع ، لماذا أرغب في معرفة مكان استخدام الفصل؟ قد أكون أكثر اهتماما في المكان الذي يتم إنشاءه وسيظل هذا البحث قابلا للبحث لأنه في مكان ما يجب أن يتم استدعاء منشئه (حتى إذا تم ذلك بطريقة غير مباشرة ، يجب ذكر اسم النوع في مكان ما).


إذا كان شخص ما يستخدم الكلمة الرئيسية var لأنها لا تريد "معرفة النوع" ، فهذا بالتأكيد هو السبب الخاطئ. لا تُنشئ الكلمة الرئيسية var متغيرًا ذا نوع ديناميكي ، فما زال المحول البرمجي بحاجة إلى معرفة النوع. بما أن المتغير يحتوي دائمًا على نوع محدد ، فيجب أن يكون النوع أيضًا واضحًا في الكود إن أمكن.

الأسباب الجيدة لاستخدام الكلمة الرئيسية var هي على سبيل المثال:

  • حيث يكون مطلوبًا ، أي إعلان مرجع لنوع مجهول.
  • حيث يجعل الشفرة أكثر قابلية للقراءة ، أي إزالة الإعلانات التكرارية.

كتابة نوع البيانات غالباً ما يجعل التعليمات البرمجية أسهل لمتابعة. يوضح أنواع البيانات التي تستخدمها ، حتى لا تضطر إلى معرفة نوع البيانات من خلال معرفة ما يفعله الكود أولاً.


الوقت الأكثر احتمالا ستحتاج إلى هذا هو لأنواع المجهولين (حيث هو مطلوب 100 ٪) ؛ ولكنه أيضًا يتجنب التكرار للحالات التافهة ، وتصبح المنظمة البحرية الدولية الخط أكثر وضوحًا. لست بحاجة إلى رؤية النوع مرتين للتهيئة البسيطة.

فمثلا:

Dictionary<string, List<SomeComplexType<int>>> data = new Dictionary<string, List<SomeComplexType<int>>>();

(من فضلك لا تقم بتحرير hscroll في أعلاه - فإنه يثبت نقطة!)

ضد:

var data = new Dictionary<string, List<SomeComplexType<int>>>();

ومع ذلك ، هناك حالات يكون فيها هذا مضللاً ، ويمكن أن يتسبب في حدوث أخطاء. كن حذراً باستخدام var إذا كان المتغير الأصلي والنوع المبدئي غير متطابقين. فمثلا:

static void DoSomething(IFoo foo) {Console.WriteLine("working happily") }
static void DoSomething(Foo foo) {Console.WriteLine("formatting hard disk...");}

// this working code...
IFoo oldCode = new Foo();
DoSomething(oldCode);
// ...is **very** different to this code
var newCode = new Foo();
DoSomething(newCode);

بالنظر إلى مدى قوة Intellisense الآن ، لست متأكدًا من أن var يصعب قراءته أكثر من المتغيرات الخاصة بالأعضاء في الصف ، أو المتغيرات المحلية في طريقة يتم تعريفها خارج منطقة الشاشة المرئية.

إذا كان لديك سطر من التعليمات البرمجية مثل

IDictionary<BigClassName, SomeOtherBigClassName> nameDictionary = new Dictionary<BigClassName, SomeOtherBigClassName>();

هل أسهل أو أصعب في القراءة من:

var nameDictionary = new Dictionary<BigClassName, SomeOtherBigClassName>();

حالة واحدة محددة حيث يكون var صعبًا: مراجعات الشفرة غير المتصلة ، وخاصة تلك التي يتم تنفيذها على الورق.

لا يمكنك الاعتماد على الماوس الفأر لذلك.


فار ليس مثل البديل على الإطلاق. لا يزال المتغير مكتوبًا بقوة ، ولكنك لا تضغط على المفاتيح للحصول عليه بهذه الطريقة. يمكنك التمرير فوقه في Visual Studio للاطلاع على النوع. إذا كنت تقرأ رمزًا مكتوبًا ، فمن الممكن أن تفكر قليلاً في تحديد نوع الكتاب. ولكن لا يوجد سوى سطر واحد يعلن ذلك والعديد من الخطوط التي تستخدمه ، لذلك لا يزال تقديم أسماء لائقة أمرًا جيدًا لتسهيل تتبع التعليمات البرمجية.

هل تستخدم Intellisense كسول؟ انها أقل الكتابة من الاسم كله. أم أن هناك أشياء أقل عملاً ولكنها لا تستحق النقد؟ أعتقد أن هناك ، وفار واحد منهم.


لا أرى ما هي الصفقة الكبيرة

var something = someMethod(); // Type of 'something' not clear <-- not to the compiler!

لا يزال لديك انتفاضة كاملة على "شيء ما" ، وبالنسبة لأي حالة غامضة لديك اختبارات الوحدة الخاصة بك ، أليس كذلك؟ ( هل؟ )

انها ليست varchar ، انها ليست قاتمة ، وبالتأكيد ليست دينامية أو ضعيفة الكتابة. إنه يوقف المجانين مثل هذا:

List<somethinglongtypename> v = new List<somethinglongtypename>();

وخفض مجموع العقل الكلي إلى:

var v = new List<somethinglongtypename>();

لطيفة ، ليست لطيفة تمامًا مثل:

v = List<somethinglongtypename>();

ولكن هذا هو ما يعتبره Boo .


لقد اعتمدنا "رمز للأشخاص ، وليس الآلات" ، على أساس افتراض أنك تقضي عدة مرات في وضع الصيانة أكثر من التطوير الجديد.

بالنسبة لي ، تستبعد الحجة القائلة بأن المحول البرمجي "يعرف" ما هو نوع المتغير - بالتأكيد ، لا يمكنك كتابة رمز غير صالح في المرة الأولى لأن المحول البرمجي يوقف شفرتك من التجميع ، ولكن عندما يقرأ المطور التالي الشفرة في غضون 6 أشهر ، يجب أن يكونوا قادرين على استنتاج ما يفعله المتغير بشكل صحيح أو غير صحيح وتحديد سبب القضايا بسرعة.

وهكذا،

var something = SomeMethod();

تم حظره وفقًا لمعايير الترميز ، ولكن يتم تشجيع ما يلي في فريقنا لأنه يزيد من سهولة القراءة:

var list = new List<KeyValuePair<string, double>>();
FillList( list );
foreach( var item in list ) {
   DoWork( item ); 
}

ما زلت أعتقد أن var يمكن أن يجعل الكود أكثر قابلية للقراءة في بعض الحالات. إذا كان لديّ فصل عميل مع خاصية "الطلبات" وأريد تعيين ذلك للمتغير ، فسأفعل ذلك فقط:

var orders = cust.Orders;

لا يهمني إذا كان Customer.Orders هو IEnumerable<Order> أو ObservableCollection<Order> أو BindingList<Order> - كل ما أريده هو الاحتفاظ بهذه القائمة في الذاكرة لتكرارها أو الحصول على حسابها أو شيء لاحق.

قارن الإعلان السابق مع:

ObservableCollection<Order> orders = cust.Orders;

بالنسبة لي ، اسم النوع هو مجرد ضجيج. وإذا عدت وقررت تغيير نوع العميل. يسير على المسار (قل من ObservableCollection<Order> إلى IList<Order> ) ثم أحتاج إلى تغيير هذا الإعلان أيضًا - وهو أمر لا يجب أن أفعله إذا كنت قد استخدمت var في المقام الأول.


هذا ليس سيئاً ، إنه شيء أكثر أسلوبية ، والتي تميل إلى أن تكون ذاتية. يمكن أن تضيف تناقضات ، عندما تستخدم var و عندما لا تفعل ذلك.

حالة أخرى مثيرة للقلق ، في المكالمة التالية لا يمكنك معرفة فقط من خلال النظر إلى رمز نوع عاد بواسطة CallMe :

var variable = CallMe();

هذا هو شكوى بلدي الرئيسي ضد فار.

أستخدم var عندما أعلن المفوضين المجهولين في الطرق ، وبصورة ما يبدو var أنظف مما لو كنت أستخدم Func . خذ بعين الاعتبار هذا الرمز:

var callback = new Func<IntPtr, bool>(delegate(IntPtr hWnd) {
   ...
});

تعديل: تحديث نموذج التعليمات البرمجية الأخير استناداً إلى إدخال Julian


@aku: One example is code reviews. Another example is refactoring scenarios.

Basically I don't want to go type-hunting with my mouse. It might not be available.


I guess it depends on your perspective. I personally have never had any difficulty understanding a piece of code because of var "misuse", and my coworkers and I use it quite a lot all over. (I agree that Intellisense is a huge aid in this regard.) I welcome it as a way to remove repetitive cruft.

After all, if statements like

var index = 5; // this is supposed to be bad

var firstEligibleObject = FetchSomething(); // oh no what type is it
                                            // i am going to die if i don't know

were really that impossible to deal with, nobody would use dynamically typed languages.


I only use var when it's clear to see what type is used.

For example, I would use var in this case, because you can see immediately that x will be of the type "MyClass":

var x = new MyClass();

I would NOT use var in cases like this, because you have to drag the mouse over the code and look at the tooltip to see what type MyFunction returns:

var x = MyClass.MyFunction();

Especially, I never use var in cases where the right side is not even a method, but only a value:

var x = 5;

(because the compiler can't know if I want a byte, short, int or whatever)


In your comparison between IEnumerable<int> and IEnumerable<double> you don't need to worry - if you pass the wrong type your code won't compile anyway.

There's no concern about type-safety, as var is not dynamic. It's just compiler magic and any type unsafe calls you make will get caught.

Var is absolutely needed for Linq:

var anonEnumeration =
    from post in AllPosts()
    where post.Date > oldDate
    let author = GetAuthor( post.AuthorId )
    select new { 
        PostName = post.Name, 
        post.Date, 
        AuthorName = author.Name
    };

Now look at anonEnumeration in intellisense and it will appear something like IEnumerable<'a>

foreach( var item in anonEnumeration ) 
{
    //VS knows the type
    item.PostName; //you'll get intellisense here

    //you still have type safety
    item.ItemId;   //will throw a compiler exception
}

The C# compiler is pretty clever - anon types generated separately will have the same generated type if their properties match.

Outside of that, as long as you have intellisense it makes good sense to use var anywhere the context is clear.

//less typing, this is good
var myList = new List<UnreasonablyLongClassName>();

//also good - I can't be mistaken on type
var anotherList = GetAllOfSomeItem();

//but not here - probably best to leave single value types declared
var decimalNum = 123.456m;

It can certainly make things simpler, from code I wrote yesterday:

var content  = new Queue<Pair<Regex, Func<string, bool>>>();
...
foreach (var entry in content) { ... }

This would have be extremely verbose without var .

Addendum: A little time spent with a language with real type inference (eg F#) will show just how good compilers are at getting the type of expressions right. It certainly has meant I tend to use var as much as I can, and using an explicit type now indicates that the variable is not of the initialising expression's type.


Many time during testing, I find myself having code like this:

var something = myObject.SomeProperty.SomeOtherThing.CallMethod();
Console.WriteLine(something);

Now, sometimes, I'll want to see what the SomeOtherThing itself contains, SomeOtherThing is not the same type that CallMethod() returns. Since I'm using var though, I just change this:

var something = myObject.SomeProperty.SomeOtherThing.CallMethod();

الى هذا:

var something = myObject.SomeProperty.SomeOtherThing;

Without var, I'd have to keep changing the declared type on the left hand side as well. I know it's minor, but it's extremely convenient.



To me, the antipathy towards var illustrates why bilingualism in .NET is important. To those C# programmers who have also done VB .NET, the advantages of var are intuitively obvious. The standard C# declaration of:

List<string> whatever = new List<string>();

is the equivalent, in VB .NET, of typing this:

Dim whatever As List(Of String) = New List(Of String)

Nobody does that in VB .NET, though. It would be silly to, because since the first version of .NET you've been able to do this...

Dim whatever As New List(Of String)

...which creates the variable and initializes it all in one reasonably compact line. Ah, but what if you want an IList<string> , not a List<string> ? Well, in VB .NET that means you have to do this:

Dim whatever As IList(Of String) = New List(Of String)

Just like you'd have to do in C#, and obviously couldn't use var for:

IList<string> whatever = new List<string>();

If you need the type to be something different, it can be. But one of the basic principles of good programming is reducing redundancy, and that's exactly what var does.


Use it for anonymous types - that's what it's there for. Anything else is a use too far. Like many people who grew up on C, I'm used to looking at the left of the declaration for the type. I don't look at the right side unless I have to. Using var for any old declaration makes me do that all the time, which I personally find uncomfortable.

Those saying 'it doesn't matter, use what you're happy with' are not seeing the whole picture. Everyone will pick up other people's code at one point or another and have to deal with whatever decisions they made at the time they wrote it. It's bad enough having to deal with radically different naming conventions, or - the classic gripe - bracing styles, without adding the whole ' var or not' thing into the mix. The worst case will be where one programmer didn't use var and then along comes a maintainer who loves it, and extends the code using it. So now you have an unholy mess.

Standards are a good thing precisely because they mean you're that much more likely to be able to pick up random code and be able to grok it quickly. The more things that are different, the harder that gets. And moving to the 'var everywhere' style makes a big difference.

I don't mind dynamic typing, and I don't mind implict typing - in languages that are designed for them. I quite like Python. But C# was designed as a statically explicitly-typed language and that's how it should stay. Breaking the rules for anonymous types was bad enough; letting people take that still further and break the idioms of the language even more is something I'm not happy with. Now that the genie is out of the bottle, it'll never go back in. C# will become balkanised into camps. Not good.





var