How to: Know the Difference Between Passing a Struct and Passing a Class Reference to a Method class public public
Учебные материалы


How to: Know the Difference Between Passing a Struct and Passing a Class Reference to a Method



How to: Know the Difference Between Passing a Struct and Passing a Class Reference to a Method


This example shows that when a struct is passed to a method, a copy of the struct is passed, but when a class instance is passed, a reference is passed.
The output of the following example shows that only the value of the class field is changed when the class instance is passed to the ClassTaker method. The struct field, however, does not change by passing its instance to the StructTaker method. This is because a copy of the struct is passed to the StructTaker method, while a reference to the class is passed to the ClassTaker method.

Example


class

TheClass
{

public

string willIChange;
}

struct

TheStruct
{

public

string willIChange;
}

class

TestClassAndStruct
{

static

void

ClassTaker(TheClass c)
{
c.willIChange = "Changed";
}

static

void

StructTaker(TheStruct s)
{
s.willIChange = "Changed";
}

static

void

Main()
{
TheClass testClass =

new

TheClass();
TheStruct testStruct =

new

TheStruct();
testClass.willIChange = "Not Changed";
testStruct.willIChange = "Not Changed";
ClassTaker(testClass);
StructTaker(testStruct);
System.Console.WriteLine("Class field = {0}", testClass.willIChange);
System.Console.WriteLine("Struct field = {0}", testStruct.willIChange);
}
}

Class field = Changed
Struct field = Not Changed

Определение различия между передачей структуры и ссылки класса в метод


В этом примере показывается, что при передаче структуры в метод передается копия структуры, а при передаче экземпляра класса передается ссылка.
В результате выполнения следующего примера видно, что при передаче экземпляра класса в метод ClassTaker изменяется только значение в поле класса. Однако при передаче экземпляра структуры в метод StructTaker ее поле не изменяется. Это происходит потому, что в метод StructTaker передается копия структуры, а в метод ClassTaker передается ссылка на класс.

Пример


----

Anonymous Types


Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to first explicitly define a type. The type name is generated by the compiler and is not available at the source code level. The type of the properties is inferred by the compiler. The following example shows an anonymous type being initialized with two properties called Amount and Message.

var v = new { Amount = 108, Message = "Hello" };

Anonymous types are typically used in the select clause of a query expression to return a subset of the properties from each object in the source sequence.
Anonymous types are created by using the new operator with an object initializer.
Anonymous types are class types that consist of one or more public read-only properties. No other kinds of class members such as methods or events are allowed.
The most common scenario is to initialize an anonymous type with some properties from another type. In the following example, assume a class that is named Product that includes Color and Price properties together with several other properties that you are not interested in. Products is a collection of Product objects. The anonymous type declaration starts with the

new

keyword. It initializes a new type that uses only two properties from Product. This causes a smaller amount of data to be returned in the query.
If you do not specify member names in the anonymous type, the compiler gives the anonymous type members the same name as the property being used to initialize them. You must provide a name to a property that is being initialized with an expression.

Анонимные типы41


Анонимные типы предлагают удобный способ инкапсулирования набора свойств только для чтения в один объект без необходимости предварительного явного определения типа. Имя типа создается компилятором и недоступно на уровне исходного кода. Тип свойств выводится компилятором. В следующем примере показан анонимный тип, который инициализируется двумя свойствами: Amount и Message.

var v = new { Amount = 108, Message = "Hello" };

Анонимные типы обычно используются в предложении select выражения запроса для возврата подмножества свойств из каждого объекта в исходной последовательности.
Анонимные типы создаются при помощи оператора new с инициализатором объекта. Дополнительные сведения об инициализаторах объектов см. в разделе Инициализаторы объектов и коллекций.
Анонимные типы — это типы class, состоящие из одного или более открытых свойств только для чтения. Все остальные виды членов класса, такие как методы или события, запрещены.
Обычно анонимный тип инициализируется определенными свойствами другого типа. В приведенном ниже примере используется класс Product, включающий свойства Color и Price, а также несколько других свойств, которые нас не интересуют. Products — это коллекция объектов Product. Анонимные объявления типа начинаются с ключевого слова

new

. Оно инициализирует новый тип, который использует только два свойства из класса Product. Это приводит к уменьшению объема возвращаемых данных.
Если не указать имена членов в анонимном типе, компилятор присвоит этим членам имя свойства, которое используется для их инициализации. Для свойства, которое инициализируется выражением, необходимо указывать имя.

var productQuery =

from

prod

in

products

select

new

{ prod.Color, prod.Price };

foreach

(var v

in

productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

When an anonymous type is assigned to a variable, that variable must be initialized with the var construct. This is because only the compiler has access to the underlying name of the anonymous type.

Remarks


Anonymous types are reference types that derive directly from object. The compiler gives them a name although your application cannot access it. From the perspective of the common language runtime, an anonymous type is no different from any other reference type.
If two or more anonymous types have the same number and type of properties in the same order, the compiler treats them as the same type and they share the same compiler-generated type information.
An anonymous type has method scope. To pass an anonymous type, or a collection that contains anonymous types, outside a method boundary, you must first cast the type to object. However, this defeats the strong typing of the anonymous type. If you must store your query results or pass them outside the method boundary, consider using an ordinary named struct or class instead of an anonymous type.
Anonymous types cannot contain unsafe types as properties.
Because the Equals and GetHashCode methods on anonymous types are defined in terms of the

Equals

and

GetHashcode

of the properties, two instances of the same anonymous type are equal only if all their properties are equal.
----
Если переменной назначается анонимный тип, эта переменная должна инициализироваться конструкцией var. Это объясняется тем, что доступ к базовому имени анонимного типа имеет только компилятор. Дополнительные сведения о

var

см. в разделе Неявно типизированные локальные переменные.

Заметки


Анонимные типы являются ссылочными типами, которые происходят непосредственно от объекта. Компилятор присваивает им имена, несмотря на то что они недоступны для вашего приложения. С точки зрения среды CLR анонимный тип ничем не отличается от любого другого ссылочного типа.
Если два или более анонимных типа имеют одинаковые номера и типы свойств одного порядка, компилятор обрабатывает их как один и тот же тип, в результате чего им присваивается одна и та же информация о типе, созданная компилятором.
Анонимный тип имеет область действия метода. Для передачи анонимного типа или коллекции анонимных типов за границы метода необходимо прежде всего привести тип к объекту. Однако это нарушает строгое определение анонимного типа. Если результаты запроса нужно сохранить или передать за границы метода, попробуйте воспользоваться обычной именованной структурой или классом вместо анонимного типа.
Анонимные типы не могут содержать небезопасные типы в качестве свойств.
Поскольку методы Equals и GetHashCode анонимных типов определены через

Equals

и

GetHashcode

свойств, два экземпляра одного анонимного типа будут идентичны только тогда, когда идентичны все их свойства.

How to: Return Subsets of Element Properties in a Query


Use an anonymous type in a query expression when both of these conditions apply:

  • You want to return only some of the properties of each source element.

  • You do not have to store the query results outside the scope of the method in which the query is executed.

If you only want to return one property or field from each source element, then you can just use the dot operator in the

select

clause. For example, to return only the ID of each student, write the

select

clause as follows:

select student.ID;
Example

The following example shows how to use an anonymous type to return only a subset of the properties of each source element that matches the specified condition.

private

static

void

QueryByScore()
{
// Create the query. var is required because
// the query produces a sequence of anonymous types.
var queryHighScores =

from

student

in

students

where

student.ExamScores[0] > 95

select

new

{ student.FirstName, student.LastName };
// Execute the query.

foreach

(var obj

in

queryHighScores)
{
// The anonymous type's properties were not named. Therefore
// they have the same names as the Student properties.
Console.WriteLine(obj.FirstName + ", " + obj.LastName);
}
}
/* Output:
Adams, Terry
Fakhouri, Fadi
Garcia, Cesar
Omelchenko, Svetlana
Zabokritski, Eugene
*/

Возвращение поднаборов свойств элементов в запросе


Используйте анонимный тип в выражении запроса, если выполняются оба этих условия:

  • Требуется возвратить только некоторые свойства каждого из исходных элементов.

  • Не требуется сохранять результаты запроса за пределами области действия метода, в котором был выполнен запрос.

Если требуется возвратить одно свойство или поле из каждого исходного элемента, можно просто воспользоваться оператором "точка" в предложении

select

. Например, чтобы возвратить только ID для каждого student, создайте следующее предложение

select

:

select student.ID;
Пример

В следующем примере показывается, как использовать анонимный тип для возврата только поднабора свойств каждого из исходных элементов, соответствующих указанному условию.
----
Note that the anonymous type uses the source element's names for its properties if no names are specified. To give new names to the properties in the anonymous type, write the

select

statement as follows:

select new { First = student.FirstName, Last = student.LastName };

If you try this in the previous example, then the Console.WriteLine statement must also change:

Console.WriteLine(student.First + " " + student.Last);
Compiling the Code

  • To run this code, copy and paste the class into a Visual C# console application project that has been created in Visual Studio. By default, this project targets version 3.5 of the .NET Framework, and it will have a reference to System.Core.dll and a

    using

    directive for System.Linq. If one or more of these requirements are missing from the project, you can add them manually.

Обратите внимание, что анонимный тип использует имена исходного элемента для его свойств, если имена не заданы. Чтобы назначить свойствам в анонимном типе новые имена, укажите следующий оператор

select

:

select new { First = student.FirstName, Last = student.LastName };

Если попытаться вставить его в предыдущий пример, то потребуется изменить и оператор Console.WriteLine:

Console.WriteLine(student.First + " " + student.Last);
Компиляция кода

  • Для выполнения этого кода скопируйте класс в проект консольного приложения на языке Visual C#, которое было создано в среде разработки Visual Studio. По умолчанию этот проект предназначен для версии 3.5 платформы .NET Framework и имеет ссылку на библиотеку System.Core.dll и директиву

    using

    для System.Linq. Если одно или более требований в проекте отсутствуют, их можно добавить вручную.

Extension Methods


Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type. For client code written in C# and Visual Basic, there is no apparent difference between calling an extension method and the methods that are actually defined in a type.
The most common extension methods are the LINQ standard query operators that add query functionality to the existing System.Collections..::.IEnumerable and System.Collections.Generic..::.IEnumerable<(Of )>) types. To use the standard query operators, first bring them into scope with a using System.Linq directive. Then any type that implements IEnumerable<(Of )>) appears to have instance methods such as GroupBy, OrderBy, Average, and so on. You can see these additional methods in IntelliSense statement completion when you type "dot" after an instance of an IEnumerable<(Of )>) type such as List<(Of )>) or Array. 
The following example shows how to call the standard query operator

OrderBy

method on an array of integers. The expression in parentheses is a lambda expression. Many standard query operators take lambda expressions as parameters, but this is not a requirement for extension methods.

class

ExtensionMethods2
{

static

void

Main()
{
int[] ints = { 10, 45, 15, 39, 21, 26 };
var result = ints.OrderBy(g => g);

foreach

(var i

in

result)
{
System.Console.Write(i + " ");
}
}
}
//Output: 10 15 21 26 39 45

Extension methods are defined as static methods but are called by using instance method syntax. Their first parameter specifies which type the method operates on, and the parameter is preceded by the this modifier. Extension methods are only in scope when you explicitly import the namespace into your source code with a

using

directive.

Методы расширения


Метода расширения позволяют "добавлять" методы в существующие типы без создания нового производного типа, перекомпиляции или иного изменения исходного типа. Методы расширения являются особым видом статического метода, но они вызываются, как если бы они были методами экземпляра в расширенном типе. Для клиентского кода, написанного на языках C# и Visual Basic, нет видимого различия между вызовом метода расширения и вызовом методов, фактически определенных в типе.
Наиболее распространенными методами расширения являются стандартные операторы запроса LINQ, добавляющие функции запроса в существующие типы System.Collections..::.IEnumerable и System.Collections.Generic..::.IEnumerable<(Of )>). Для использования стандартных операторов запроса их сначала надо добавить в область видимости с помощью директивы using System.Linq. Затем каждый тип, который реализует тип IEnumerable<(Of )>), будет иметь методы экземпляра, такие как GroupBy, OrderBy, Average и т.д. Эти дополнительные методы можно видеть в завершении операторов IntelliSense, когда вводится точка после экземпляра типа IEnumerable<(Of )>), например List<(Of )>) или Array.
В следующем примере показано, как вызывать метод стандартного оператора запроса

OrderBy

для массива целых чисел. Выражение в скобках называется лямбда-выражением. Многие стандартные операторы запроса принимают лямбда-выражения в качестве параметров, но это не является обязательным для методов расширения. Дополнительные сведения см. в разделе Лямбда-выражения.
------
Методы расширения определяются как статические методы, но вызываются с помощью синтаксиса обращения к методу экземпляра. Их первый параметр определяет, с каким типом оперирует метод, и перед параметром идет модификатор this. Методы расширения находятся в области действия, только если пространство имен было явно импортировано в исходный код с помощью директивы

using

.
The following example shows an extension method defined for the System..::.String class. Note that it is defined inside a non-nested, non-generic static class:

namespace

ExtensionMethods
{

public

static

class

MyExtensions
{

public

static

int WordCount(

this

String str)
{

return

str.Split(

new

char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}

The WordCount extension method can be brought into scope with this

using

directive:

using ExtensionMethods;

And it can be called from an application by using this syntax:

string s = "Hello Extension Methods";
int i = s.WordCount();

In your code you invoke the extension method with instance method syntax. However, the intermediate language (IL) generated by the compiler translates your code into a call on the static method. Therefore, the principle of encapsulation is not really being violated. In fact, extension methods cannot access private variables in the type they are extending.
In general, you will probably be calling extension methods far more often than implementing your own. Because extension methods are called by using instance method syntax, no special knowledge is required to use them from client code. To enable extension methods for a particular type, just add a

using

directive for the namespace in which the methods are defined. For example, to use the standard query operators, add this

using

directive to your code:

using System.Linq;

(You may also have to add a reference to System.Core.dll.) You will notice that the standard query operators now appear in IntelliSense as additional methods available for most IEnumerable<(Of )>) types.

Note:



Although standard query operators do not appear in IntelliSense for String, they are still available.

В приведенном ниже примере показан метод расширения, определяемый для класса System..::.String. Обратите внимание, что этот метод определяется внутри не вложенного, не универсального статического класса.
---
Метод расширения WordCount может быть добавлен в область действия с помощью директивы

using

.

using ExtensionMethods;

И он может вызываться из приложения с помощью следующего синтаксиса.

string s = "Hello Extension Methods";
int i = s.WordCount();

В созданном коде метод расширения вызывается с помощью синтаксиса обращения к метода экземпляра. Однако промежуточный язык (IL), генерируемый компилятором, переводит созданный код в вызов статического метода. Поэтому принцип инкапсуляции фактически не нарушается. В действительности, методы расширения не могут получить доступ к закрытым переменным типа, для расширения которого они используются.
Вообще, обычно гораздо чаще приходится вызывать методы расширения, чем реализовывать собственные методы. Так как методы расширения вызываются с помощью синтаксиса обращения к методу экземпляра, для использования их из клиентского кода специальные знания не требуются. Чтобы включить методы расширения для определенного типа необходимо просто добавить директиву

using

для пространства имен, в котором эти методы определяются. Например, чтобы использовать стандартные операторы запроса, необходимо добавить директиву

using

в создаваемый код.

using System.Linq;

(Также может потребоваться добавить ссылку на библиотеку System.Core.dll.) Обратите внимание, что стандартные операторы запроса теперь появляются в списке IntelliSense в виде дополнительных методов, доступных для большинства типов IEnumerable<(Of )>).

Примечание.



Хотя стандартные операторы запросов не появляются в списке IntelliSense для типа String, они все равно доступны.

Implicitly Typed Local Variables


Local variables can be given an inferred "type" of

var

instead of an explicit type. The

var

keyword instructs the compiler to infer the type of the variable from the expression on the right side of the initialization statement. The inferred type may be a built-in type, an anonymous type, a user-defined type, a type defined in the .NET Framework class library, or any expression.
The following examples show various ways in which local variables can be declared with

var

:

// i is compiled as an int
var i = 5;
// s is compiled as a string
var s = "Hello";
// a is compiled as int[]
var a =

new

[] { 0, 1, 2 };
// expr is compiled as IEnumerable
var expr =

from

c

in

customers

where

c.City == "London"

select

c;
// anon is compiled as an anonymous type
var anon =

new

{ Name = "Terry", Age = 34 };
// list is compiled as List
var list =

new

List();

It is important to understand that the

var

keyword does not mean “Variant” and does not indicate that the variable is loosely typed, or late-bound. It just means that the compiler determines and assigns the most appropriate type.
The

var

keyword may be used in the following contexts:

  • On local variables (variables declared at method scope) as shown in the previous example.

  • In a for initialization statement.

    for(var x = 1; x < 10; x++)

  • In a foreach initialization statement.

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

  • In a using Statement

using (var file = new StreamReader("C:\\myfile.txt")) {...}

Неявно типизированные локальные переменные


Локальным переменным вместо явного типа может быть задан определенный "тип"

var

. Ключевое слово

var

сообщает компилятору необходимости определения типа переменной из выражения, находящегося с правой стороны оператора инициализации. Определенный тип может быть встроенным, анонимным, определенным пользователем типом, типом, определенным в библиотеке классов, или любым выражением. Дополнительные сведения об инициализации массивов с

var

см. в разделе Неявно типизированные массивы.
В следующем примере показаны разные способы объявления локальных переменных с

var

.
---
Важно понять, что ключевое слово

var

не означает "вариант" и не указывает на свободную типизацию или позднюю привязку переменной. Оно просто значит, что компилятор определяет и назначает наиболее подходящий тип.
Ключевое слово

var

можно использовать в следующих контекстах:

  • в локальных переменных (переменных, объявленных в области действия метода), как показано в предыдущем примере;

  • в операторе инициализации for;

    for(var x = 1; x < 10; x++)

  • в операторе инициализации for;

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

  • в операторе операторе using.

using (var file = new StreamReader("C:\\myfile.txt")) {...}

var and Anonymous Types


In many cases the use of

var

is optional and is just a syntactic convenience. However, it is required when a variable is initialized with an anonymous type. This is a common scenario in LINQ query expressions.
Because the name of an anonymous type is known only to the compiler, you must use

var

in your source code. If a query variable has been initialized with

var

, then you must also use

var

as the type of the iteration variable in the

foreach

statement that iterates over the query variable.

class

ImplicitlyTypedLocals2
{

static

void

Main()
{
string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };
// If a query produces a sequence of anonymous types,
// then you must also use var in the foreach statement.
var upperLowerWords =

from

w

in

words

select

new

{ Upper = w.ToUpper(), Lower = w.ToLower() };
// Execute the query

foreach

(var ul

in

upperLowerWords)
{
Console.WriteLine("Uppercase: {0}, Lowercase: {1}", ul.Upper, ul.Lower);
}
}
}
/* Outputs:
Uppercase: APPLE, Lowercase: apple
Uppercase: BLUEBERRY, Lowercase: blueberry
Uppercase: CHERRY, Lowercase: cherry
*/

var и анонимные типы


Во многих случаях использование ключевого слова

var

является необязательным, оно служит лишь для синтаксического удобства. Однако это ключевое слово необходимо при инициализации переменной с анонимным типом. Этот сценарий является типичным в выражениях запроса LINQ. Дополнительные сведения см. в разделе Анонимные типы.
Поскольку имя анонимного типа известно только компилятору, в исходном коде следует использовать ключевое слово

var

. Если переменная запроса инициализирована с

var

, ключевое слово

var

также должно использоваться в качестве типа переменной итерации в операторе

foreach

, который выполняет итерацию по переменной запроса.
-----

Remarks


The following restrictions apply to implicitly-typed variable declarations:

  • var

    can only be used when a local variable is declared and initialized in the same statement; the variable cannot be initialized to null.

  • var

    cannot be used on fields at class scope.

  • Variables declared by using

    var

    cannot be used in the initialization expression. In other words, var v = v++; produces a compile-time error.

  • Multiple implicitly-typed variables cannot be initialized in the same statement.

  • If a type named var is in scope, then you will get a compile-time error if you try to initialize a local variable with the

    var

    keyword.

The only scenario in which a local variable must be implicitly typed with var is in the initialization of anonymous types.
You may find that

var

can also be useful with query expressions in which the exact constructed type of the query variable is difficult to determine. This can occur with grouping and ordering operations.
The

var

keyword can also be useful when the specific type of the variable is tedious to type on the keyboard, or is obvious, or does not add to the readability of the code. One example where

var

is helpful in this manner is with nested generic types such as those used with group operations. In the following query, the type of the query variable is

IEnumerable

. As long as you and others who must maintain your code understand this, there is no problem with using implicit typing for convenience and brevity.

// Same as previous example except we use the entire last name as a key.
// Query variable is an IEnumerable
var studentQuery3 =

from

student

in

students

group

student

by

student.Last;

However, the use of

var

does have at least the potential to make your code more difficult to understand for other developers. For that reason, the C# documentation generally uses

var

only when it is required.

Заметки


К объявлениям неявно типизированных переменных применимы следующие ограничения.

  • var

    может использоваться, только если локальная переменная объявлена и инициализирована в одном операторе; переменная не может быть инициализирована значением NULL.

  • var

    не может использоваться в полях в области действия класса.

  • Переменные, объявленные с помощью

    var

    , не могут использоваться в выражении инициализации. Другими словами, var v = v++; вызывает ошибку времени компиляции.

  • В одном операторе нельзя инициализировать несколько неявно типизированных переменных.

  • Если тип с именем var находится в области действия, при попытке инициализировать локальную переменную с

    var

    возникнет ошибка времени компилятора.

Инициализация анонимного типа является единственным сценарием, когда локальная переменная должна быть явно типизирована с var. Дополнительные сведения см. в разделе Анонимные типы.
Использование ключевого слова

var

может оказаться целесообразным в выражениях запроса, где определение точного конструируемого типа переменной запроса является сложной задачей. Это может происходить во время группировки и сортировки.
Ключевое слово

var

удобно, когда определенный тип переменной сложно вводить с клавиатуры, либо он является очевидным, либо не повышает удобочитаемость кода. Примером подобного применения ключевого слова

var

служит ситуация со вложенными универсальными типами, которые используются операциях по группировке. В следующем запросе используется переменная запроса с типом

IEnumerable

. При условии, что пользователи, работающие с кодом, понимают данные принципы, не возникнет никаких проблем с использованием неявной типизации для удобства и краткости.
----
Однако использование ключевого слова

var

может сделать код более трудным для понимания другими разработчиками. По этой причине в документации по C# ключевое слово

var

обычно используется только при необходимости.

Последнее изменение этой страницы: 2018-09-09;


dommodels.ru 2018 год. Все права принадлежат их авторам! Главная