C# Programming Guide - Types 프로그래밍/C#2017. 5. 10. 17:01
- Types
- Casting and Type Conversions
- Boxing and Unboxing
- Using Type dynamic
- How to: Safely Cast by Using as and is Operators
- How to: Convert a byte Array to an int
- How to: Convert a String to a Number
- How to: Convert Between Hexadecimal Strings and Numeric Types
https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/types/index
Types
Types, Variables, and Values
C# is a strongly-typed language. Every variable and constant has a type, as does every expression that evaluates to a value. Every method signature specifies a type for each input parameter and for the return value. The .NET Framework class library defines a set of built-in numeric types as well as more complex types that represent a wide variety of logical constructs, such as the file system, network connections, collections and arrays of objects, and dates. A typical C# program uses types from the class library as well as user-defined types that model the concepts that are specific to the program's problem domain.
The information stored in a type can include the following:
The storage space that a variable of the type requires.
The maximum and minimum values that it can represent.
The members (methods, fields, events, and so on) that it contains.
The base type it inherits from.
The location where the memory for variables will be allocated at run time.
The kinds of operations that are permitted.
The compiler uses type information to make sure that all operations that are performed in your code are type safe. For example, if you declare a variable of type
, the compiler allows you to use the variable in addition and subtraction operations. If you try to perform those same operations on a variable of type , the compiler generates an error, as shown in the following example:int a = 5;
int b = a + 2; //OK
bool test = true;
// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;
Note
C and C++ developers, notice that in C#,
is not convertible to .The compiler embeds the type information into the executable file as metadata. The common language runtime (CLR) uses that metadata at run time to further guarantee type safety when it allocates and reclaims memory.
Specifying Types in Variable Declarations
When you declare a variable or constant in a program, you must either specify its type or use the
keyword to let the compiler infer the type. The following example shows some variable declarations that use both built-in numeric types and complex user-defined types:// Declaration only:
float temperature;
string name;
MyClass myClass;
// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = { 0, 1, 2, 3, 4, 5 };
var query = from item in source
where item <= limit
select item;
The types of method parameters and return values are specified in the method signature. The following signature shows a method that requires an
as an input argument and returns a string:public string GetName(int ID)
{
if (ID < names.Length)
return names[ID];
else
return String.Empty;
}
private string[] names = { "Spencer", "Sally", "Doug" };
After a variable is declared, it cannot be re-declared with a new type, and it cannot be assigned a value that is not compatible with its declared type. For example, you cannot declare an
and then assign it a Boolean value of . However, values can be converted to other types, for example when they are assigned to new variables or passed as method arguments. A type conversion that does not cause data loss is performed automatically by the compiler. A conversion that might cause data loss requires a cast in the source code.For more information, see
.Built-in Types
C# provides a standard set of built-in numeric types to represent integers, floating point values, Boolean expressions, text characters, decimal values, and other types of data. There are also built-in string
and object
types. These are available for you to use in any C# program. For a more information about the built-in types, see .
Custom Types
You use the
, , , and constructs to create your own custom types. The .NET Framework class library itself is a collection of custom types provided by Microsoft that you can use in your own applications. By default, the most frequently used types in the class library are available in any C# program. Others become available only when you explicitly add a project reference to the assembly in which they are defined. After the compiler has a reference to the assembly, you can declare variables (and constants) of the types declared in that assembly in source code. For more information, see .The Common Type System
It is important to understand two fundamental points about the type system in the .NET Framework:
It supports the principle of inheritance. Types can derive from other types, called base types. The derived type inherits (with some restrictions) the methods, properties, and other members of the base type. The base type can in turn derive from some other type, in which case the derived type inherits the members of both base types in its inheritance hierarchy. All types, including built-in numeric types such as
(C# keyword: ), derive ultimately from a single base type, which is (C# keyword: ). This unified type hierarchy is called the (CTS). For more information about inheritance in C#, see .Each type in the CTS is defined as either a value type or a reference type. This includes all custom types in the .NET Framework class library and also your own user-defined types. Types that you define by using the
keyword are value types; all the built-in numeric types arestructs
. Types that you define by using the keyword are reference types. Reference types and value types have different compile-time rules, and different run-time behavior.
The following illustration shows the relationship between value types and reference types in the CTS.
Value types and reference types in the CTS
Note
You can see that the most commonly used types are all organized in the
namespace. However, the namespace in which a type is contained has no relation to whether it is a value type or reference type.Value Types
Value types derive from
, which derives from . Types that derive from have special behavior in the CLR. Value type variables directly contain their values, which means that the memory is allocated inline in whatever context the variable is declared. There is no separate heap allocation or garbage collection overhead for value-type variables.There are two categories of value types:
and .The built-in numeric types are structs, and they have properties and methods that you can access:
// Static method on type Byte.
byte b = Byte.MaxValue;
But you declare and assign values to them as if they were simple non-aggregate types:
byte num = 0xA;
int i = 5;
char c = 'Z';
Value types are sealed, which means, for example, that you cannot derive a type from
, and you cannot define a struct to inherit from any user-defined class or struct because a struct can only inherit from . However, a struct can implement one or more interfaces. You can cast a struct type to an interface type; this causes a boxing operation to wrap the struct inside a reference type object on the managed heap. Boxing operations occur when you pass a value type to a method that takes a as an input parameter. For more information, see .You use the
keyword to create your own custom value types. Typically, a struct is used as a container for a small set of related variables, as shown in the following example:public struct CoOrds
{
public int x, y;
public CoOrds(int p1, int p2)
{
x = p1;
y = p2;
}
}
For more information about structs, see
. For more information about value types in the .NET Framework, see .The other category of value types is
. An enum defines a set of named integral constants. For example, the enumeration in the .NET Framework class library contains a set of named constant integers that specify how a file should be opened. It is defined as shown in the following example:public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
The System.IO.FileMode.Create
constant has a value of 2. However, the name is much more meaningful for humans reading the source code, and for that reason it is better to use enumerations instead of constant literal numbers. For more information, see .
All enums inherit from
, which inherits from . All the rules that apply to structs also apply to enums. For more information about enums, see .Reference Types
A type that is defined as a new, as shown in the following example:
MyClass mc = new MyClass();
MyClass mc2 = mc;
An interface must be initialized together with a class object that implements it. If MyClass
implements IMyInterface
, you create an instance of IMyInterface
as shown in the following example:
IMyInterface iface = new MyClass();
When the object is created, the memory is allocated on the managed heap, and the variable holds only a reference to the location of the object. Types on the managed heap require overhead both when they are allocated and when they are reclaimed by the automatic memory management functionality of the CLR, which is known as garbage collection. However, garbage collection is also highly optimized, and in most scenarios it does not create a performance issue. For more information about garbage collection, see
.All arrays are reference types, even if their elements are value types. Arrays implicitly derive from the
class, but you declare and use them with the simplified syntax that is provided by C#, as shown in the following example:// Declare and initialize an array of integers.
int[] nums = { 1, 2, 3, 4, 5 };
// Access an instance property of System.Array.
int len = nums.Length;
Reference types fully support inheritance. When you create a class, you can inherit from any other interface or class that is not defined as
, and other classes can inherit from your class and override your virtual methods. For more information about how to create your own classes, see . For more information about inheritance and virtual methods, see .Types of Literal Values
In C#, literal values receive a type from the compiler. You can specify how a numeric literal should be typed by appending a letter to the end of the number. For example, to specify that the value 4.56 should be treated as a float, append an "f" or "F" after the number: 4.56f
. If no letter is appended, the compiler will infer a type for the literal. For more information about which types can be specified with letter suffixes, see the reference pages for individual types in .
Because literals are typed, and all types derive ultimately from
, you can write and compile code such as the following:string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);
Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);
Generic Types
A type can be declared with one or more type parameters that serve as a placeholder for the actual type (the concrete type) that client code will provide when it creates an instance of the type. Such types are called generic types. For example, the .NET Framework type
has one type parameter that by convention is given the name T. When you create an instance of the type, you specify the type of the objects that the list will contain, for example, string:List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);
The use of the type parameter makes it possible to reuse the same class to hold any type of element, without having to convert each element to strings
object in the previous example. For more information, see .
Implicit Types, Anonymous Types, and Nullable Types
As stated previously, you can implicitly type a local variable (but not class members) by using the
keyword. The variable still receives a type at compile time, but the type is provided by the compiler. For more information, see .In some cases, it is inconvenient to create a named type for simple sets of related values that you do not intend to store or pass outside method boundaries. You can create anonymous types for this purpose. For more information, see
.Ordinary value types cannot have a value of ?
after the type. For example, int?
is an int
type that can also have the value . In the CTS, nullable types are instances of the generic struct type . Nullable types are especially useful when you are passing data to and from databases in which numeric values might be null. For more information, see .
Related Sections
For more information, see the following topics:
Casting and Type Conversions
Because C# is statically-typed at compile time, after a variable is declared, it cannot be declared again or used to store values of another type unless that type is convertible to the variable's type. For example, there is no conversion from an integer to any arbitrary string. Therefore, after you declare i
as an integer, you cannot assign the string "Hello" to it, as is shown in the following code.
int i;
i = "Hello"; // Error: "Cannot implicitly convert type 'string' to 'int'"
However, you might sometimes need to copy a value into a variable or method parameter of another type. For example, you might have an integer variable that you need to pass to a method whose parameter is typed as double
. Or you might need to assign a class variable to a variable of an interface type. These kinds of operations are called type conversions. In C#, you can perform the following kinds of conversions:
Implicit conversions: No special syntax is required because the conversion is type safe and no data will be lost. Examples include conversions from smaller to larger integral types, and conversions from derived classes to base classes.
Explicit conversions (casts): Explicit conversions require a cast operator. Casting is required when information might be lost in the conversion, or when the conversion might not succeed for other reasons. Typical examples include numeric conversion to a type that has less precision or a smaller range, and conversion of a base-class instance to a derived class.
User-defined conversions: User-defined conversions are performed by special methods that you can define to enable explicit and implicit conversions between custom types that do not have a base class–derived class relationship. For more information, see .
Conversions with helper classes: To convert between non-compatible types, such as integers and objects, or hexadecimal strings and byte arrays, you can use the class, the class, and the
Parse
methods of the built-in numeric types, such as . For more information, see , , and .
Implicit Conversions
For built-in numeric types, an implicit conversion can be made when the value to be stored can fit into the variable without being truncated or rounded off. For example, a variable of type long
before assigning it to bigNum
.
// Implicit conversion. num long can
// hold any value an int can hold, and more!
int num = 2147483647;
long bigNum = num;
For a complete list of all implicit numeric conversions, see
.For reference types, an implicit conversion always exists from a class to any one of its direct or indirect base classes or interfaces. No special syntax is necessary because a derived class always contains all the members of a base class.
Derived d = new Derived();
Base b = d; // Always OK.
Explicit Conversions
However, if a conversion cannot be made without a risk of losing information, the compiler requires that you perform an explicit conversion, which is called a cast. A cast is a way of explicitly informing the compiler that you intend to make the conversion and that you are aware that data loss might occur. To perform a cast, specify the type that you are casting to in parentheses in front of the value or variable to be converted. The following program casts a
to an . The program will not compile without the cast.class Test
{
static void Main()
{
double x = 1234.7;
int a;
// Cast double to int.
a = (int)x;
System.Console.WriteLine(a);
}
}
// Output: 1234
For a list of the explicit numeric conversions that are allowed, see
.For reference types, an explicit cast is required if you need to convert from a base type to a derived type:
// Create a new derived type.
Giraffe g = new Giraffe();
// Implicit conversion to base type is safe.
Animal a = g;
// Explicit conversion is required to cast back
// to derived type. Note: This will compile but will
// throw an exception at run time if the right-side
// object is not in fact a Giraffe.
Giraffe g2 = (Giraffe) a;
A cast operation between reference types does not change the run-time type of the underlying object; it only changes the type of the value that is being used as a reference to that object. For more information, see
.Type Conversion Exceptions at Run Time
In some reference type conversions, the compiler cannot determine whether a cast will be valid. It is possible for a cast operation that compiles correctly to fail at run time. As shown in the following example, a type cast that fails at run time will cause an
to be thrown.
using System;
class Animal
{
public void Eat() { Console.WriteLine("Eating."); }
public override string ToString()
{
return "I am an animal.";
}
}
class Reptile : Animal { }
class Mammal : Animal { }
class UnSafeCast
{
static void Main()
{
Test(new Mammal());
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
static void Test(Animal a)
{
// Cause InvalidCastException at run time
// because Mammal is not convertible to Reptile.
Reptile r = (Reptile)a;
}
}
C# provides the
and operators to enable you to test for compatibility before actually performing a cast. For more information, see .Boxing and Unboxing
Boxing is the process of converting a to the type object
or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object and stores it on the managed heap. Unboxing extracts the value type from the object. Boxing is implicit; unboxing is explicit. The concept of boxing and unboxing underlies the C# unified view of the type system in which a value of any type can be treated as an object.
In the following example, the integer variable i
is boxed and assigned to object o
.
int i = 123;
// The following line boxes i.
object o = i;
The object o
can then be unboxed and assigned to integer variable i
:
o = 123;
i = (int)o; // unboxing
The following examples illustrate how boxing is used in C#.
// String.Concat example.
// String.Concat has many versions. Rest the mouse pointer on
// Concat in the following statement to verify that the version
// that is used here takes three object arguments. Both 42 and
// true must be boxed.
Console.WriteLine(String.Concat("Answer", 42, true));
// List example.
// Create a list of objects to hold a heterogeneous collection
// of elements.
List<object> mixedList = new List<object>();
// Add a string element to the list.
mixedList.Add("First Group:");
// Add some integers to the list.
for (int j = 1; j < 5; j++)
{
// Rest the mouse pointer over j to verify that you are adding
// an int to a list of objects. Each element j is boxed when
// you add j to mixedList.
mixedList.Add(j);
}
// Add another string and more integers.
mixedList.Add("Second Group:");
for (int j = 5; j < 10; j++)
{
mixedList.Add(j);
}
// Display the elements in the list. Declare the loop variable by
// using var, so that the compiler assigns its type.
foreach (var item in mixedList)
{
// Rest the mouse pointer over item to verify that the elements
// of mixedList are objects.
Console.WriteLine(item);
}
// The following loop sums the squares of the first group of boxed
// integers in mixedList. The list elements are objects, and cannot
// be multiplied or added to the sum until they are unboxed. The
// unboxing must be done explicitly.
var sum = 0;
for (var j = 1; j < 5; j++)
{
// The following statement causes a compiler error: Operator
// '*' cannot be applied to operands of type 'object' and
// 'object'.
//sum += mixedList[j] * mixedList[j]);
// After the list elements are unboxed, the computation does
// not cause a compiler error.
sum += (int)mixedList[j] * (int)mixedList[j];
}
// The sum displayed is 30, the sum of 1 + 4 + 9 + 16.
Console.WriteLine("Sum: " + sum);
// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30
Performance
In relation to simple assignments, boxing and unboxing are computationally expensive processes. When a value type is boxed, a new object must be allocated and constructed. To a lesser degree, the cast required for unboxing is also expensive computationally. For more information, see
.Boxing
Boxing is used to store value types in the garbage-collected heap. Boxing is an implicit conversion of a object
or to any interface type implemented by this value type. Boxing a value type allocates an object instance on the heap and copies the value into the new object.
Consider the following declaration of a value-type variable:
int i = 123;
The following statement implicitly applies the boxing operation on the variable i
:
// Boxing copies the value of i into object o.
object o = i;
The result of this statement is creating an object reference o
, on the stack, that references a value of the type int
, on the heap. This value is a copy of the value-type value assigned to the variable i
. The difference between the two variables, i
and o
, is illustrated in the following figure.
Boxing Conversion
It is also possible to perform the boxing explicitly as in the following example, but explicit boxing is never required:
int i = 123;
object o = (object)i; // explicit boxing
Description
This example converts an integer variable i
to an object o
by using boxing. Then, the value stored in the variable i
is changed from 123
to 456
. The example shows that the original value type and the boxed object use separate memory locations, and therefore can store different values.
Example
class TestBoxing
{
static void Main()
{
int i = 123;
// Boxing copies the value of i into object o.
object o = i;
// Change the value of i.
i = 456;
// The change in i does not effect the value stored in o.
System.Console.WriteLine("The value-type value = {0}", i);
System.Console.WriteLine("The object-type value = {0}", o);
}
}
/* Output:
The value-type value = 456
The object-type value = 123
*/
Unboxing
Unboxing is an explicit conversion from the type object
to a or from an interface type to a value type that implements the interface. An unboxing operation consists of:
Checking the object instance to make sure that it is a boxed value of the given value type.
Copying the value from the instance into the value-type variable.
The following statements demonstrate both boxing and unboxing operations:
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
The following figure demonstrates the result of the previous statements.
Unboxing Conversion
For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type. Attempting to unbox null
causes a . Attempting to unbox a reference to an incompatible value type causes an .
Example
The following example demonstrates a case of invalid unboxing and the resulting InvalidCastException
. Using try
and catch
, an error message is displayed when the error occurs.
class TestUnboxing
{
static void Main()
{
int i = 123;
object o = i; // implicit boxing
try
{
int j = (short)o; // attempt to unbox
System.Console.WriteLine("Unboxing OK.");
}
catch (System.InvalidCastException e)
{
System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);
}
}
}
This program outputs:
Specified cast is not valid. Error: Incorrect unboxing.
If you change the statement:
int j = (short) o;
to:
int j = (int) o;
the conversion will be performed, and you will get the output:
Unboxing OK.
Using Type dynamic
Visual C# 2010 introduces a new type, dynamic
. The type is a static type, but an object of type dynamic
bypasses static type checking. In most cases, it functions like it has type object
. At compile time, an element that is typed as dynamic
is assumed to support any operation. Therefore, you do not have to be concerned about whether the object gets its value from a COM API, from a dynamic language such as IronPython, from the HTML Document Object Model (DOM), from reflection, or from somewhere else in the program. However, if the code is not valid, errors are caught at run time.
For example, if instance method exampleMethod1
in the following code has only one parameter, the compiler recognizes that the first call to the method, ec.exampleMethod1(10, 4)
, is not valid because it contains two arguments. The call causes a compiler error. The second call to the method, dynamic_ec.exampleMethod1(10, 4)
, is not checked by the compiler because the type of dynamic_ec
is dynamic
. Therefore, no compiler error is reported. However, the error does not escape notice indefinitely. It is caught at run time and causes a run-time exception.
static void Main(string[] args)
{
ExampleClass ec = new ExampleClass();
// The following call to exampleMethod1 causes a compiler error
// if exampleMethod1 has only one parameter. Uncomment the line
// to see the error.
//ec.exampleMethod1(10, 4);
dynamic dynamic_ec = new ExampleClass();
// The following line is not identified as an error by the
// compiler, but it causes a run-time exception.
dynamic_ec.exampleMethod1(10, 4);
// The following calls also do not cause compiler errors, whether
// appropriate methods exist or not.
dynamic_ec.someMethod("some argument", 7, null);
dynamic_ec.nonexistentMethod();
}
class ExampleClass
{
public ExampleClass() { }
public ExampleClass(int v) { }
public void exampleMethod1(int i) { }
public void exampleMethod2(string str) { }
}
The role of the compiler in these examples is to package together information about what each statement is proposing to do to the object or expression that is typed as dynamic
. At run time, the stored information is examined, and any statement that is not valid causes a run-time exception.
The result of most dynamic operations is itself dynamic
. For example, if you rest the mouse pointer over the use of testSum
in the following example, IntelliSense displays the type (local variable) dynamic testSum.
dynamic d = 1;
var testSum = d + 3;
// Rest the mouse pointer over testSum in the following statement.
System.Console.WriteLine(testSum);
Operations in which the result is not dynamic
include conversions from dynamic
to another type, and constructor calls that include arguments of type dynamic
. For example, the type of testInstance
in the following declaration is ExampleClass
, not dynamic
.
var testInstance = new ExampleClass(d);
Conversion examples are shown in the following section, "Conversions."
Conversions
Conversions between dynamic objects and other types are easy. This enables the developer to switch between dynamic and non-dynamic behavior.
Any object can be converted to dynamic type implicitly, as shown in the following examples.
dynamic d1 = 7;
dynamic d2 = "a string";
dynamic d3 = System.DateTime.Today;
dynamic d4 = System.Diagnostics.Process.GetProcesses();
Conversely, an implicit conversion can be dynamically applied to any expression of type dynamic
.
int i = d1;
string str = d2;
DateTime dt = d3;
System.Diagnostics.Process[] procs = d4;
Overload Resolution with Arguments of Type dynamic
Overload resolution occurs at run time instead of at compile time if one or more of the arguments in a method call have the type dynamic
, or if the receiver of the method call is of type dynamic
. In the following example, if the only accessible exampleMethod2
method is defined to take a string argument, sending d1
as the argument does not cause a compiler error, but it does cause a run-time exception. Overload resolution fails at run time because the run-time type of d1
is int
, and exampleMethod2
requires a string.
// Valid.
ec.exampleMethod2("a string");
// The following statement does not cause a compiler error, even though ec is not
// dynamic. A run-time exception is raised because the run-time type of d1 is int.
ec.exampleMethod2(d1);
// The following statement does cause a compiler error.
//ec.exampleMethod2(7);
Dynamic Language Runtime
The dynamic language runtime (DLR) is a new API in .NET Framework 4. It provides the infrastructure that supports the dynamic
type in C#, and also the implementation of dynamic programming languages such as IronPython and IronRuby. For more information about the DLR, see .
COM Interop
Visual C# 2010 includes several features that improve the experience of interoperating with COM APIs such as the Office Automation APIs. Among the improvements are the use of the dynamic
type, and of .
Many COM methods allow for variation in argument types and return type by designating the types as object
. This has necessitated explicit casting of the values to coordinate with strongly typed variables in C#. If you compile by using the option, the introduction of the dynamic
type enables you to treat the occurrences of object
in COM signatures as if they were of type dynamic
, and thereby to avoid much of the casting. For example, the following statements contrast how you access a cell in a Microsoft Office Excel spreadsheet with the dynamic
type and without the dynamic
type.
// Before the introduction of dynamic.
((Excel.Range)excelApp.Cells[1, 1]).Value2 = "Name";
Excel.Range range2008 = (Excel.Range)excelApp.Cells[1, 1];
// After the introduction of dynamic, the access to the Value property and
// the conversion to Excel.Range are handled by the run-time COM binder.
excelApp.Cells[1, 1].Value = "Name";
Excel.Range range2010 = excelApp.Cells[1, 1];
Related Topics
Title | Description |
---|---|
Describes the usage of the dynamic keyword. | |
Provides an overview of the DLR, which is a runtime environment that adds a set of services for dynamic languages to the common language runtime (CLR). | |
Provides step-by-step instructions for creating a custom dynamic object and for creating a project that accesses an IronPython library. | |
Demonstrates how to create a project that uses named and optional arguments, the dynamic type, and other enhancements that simplify access to Office API objects. |
How to: Safely Cast by Using as and is Operators
Because objects are polymorphic, it is possible for a variable of a base class type to hold a derived type. To access the derived type's method, it is necessary to cast the value back to the derived type. However, to attempt a simple cast in these cases creates the risk of throwing an . That is why C# provides the and operators. You can use these operators to test whether a cast will succeed without causing an exception to be thrown. In general, the as
operator is more efficient because it actually returns the cast value if the cast can be made successfully. The is
operator returns only a Boolean value. It can therefore be used when you just want to determine an object's type but do not have to actually cast it.
Example
The following examples show how to use the is
and as
operators to cast from one reference type to another without the risk of throwing an exception. The example also shows how to use the as
operator with nullable value types.
class SafeCasting
{
class Animal
{
public void Eat() { Console.WriteLine("Eating."); }
public override string ToString()
{
return "I am an animal.";
}
}
class Mammal : Animal { }
class Giraffe : Mammal { }
class SuperNova { }
static void Main()
{
SafeCasting app = new SafeCasting();
// Use the is operator to verify the type.
// before performing a cast.
Giraffe g = new Giraffe();
app.UseIsOperator(g);
// Use the as operator and test for null
// before referencing the variable.
app.UseAsOperator(g);
// Use the as operator to test
// an incompatible type.
SuperNova sn = new SuperNova();
app.UseAsOperator(sn);
// Use the as operator with a value type.
// Note the implicit conversion to int? in
// the method body.
int i = 5;
app.UseAsWithNullable(i);
double d = 9.78654;
app.UseAsWithNullable(d);
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
void UseIsOperator(Animal a)
{
if (a is Mammal)
{
Mammal m = (Mammal)a;
m.Eat();
}
}
void UseAsOperator(object o)
{
Mammal m = o as Mammal;
if (m != null)
{
Console.WriteLine(m.ToString());
}
else
{
Console.WriteLine("{0} is not a Mammal", o.GetType().Name);
}
}
void UseAsWithNullable(System.ValueType val)
{
int? j = val as int?;
if (j != null)
{
Console.WriteLine(j);
}
else
{
Console.WriteLine("Could not convert " + val.ToString());
}
}
}
How to: Convert a byte Array to an int
This example shows you how to use the class to convert an array of bytes to an and back to an array of bytes. You may have to convert from bytes to a built-in data type after you read bytes off the network, for example. In addition to the method in the example, the following table lists methods in the class that convert bytes (from an array of bytes) to other built-in types.
Type returned | Method |
---|---|
bool | |
char | |
double | |
short | |
int | |
long | |
float | |
ushort | |
uint | |
ulong |
Example
This example initializes an array of bytes, reverses the array if the computer architecture is little-endian (that is, the least significant byte is stored first), and then calls the int
. The second argument to specifies the start index of the array of bytes.
Note
The output may differ depending on the endianess of your computer's architecture.
byte[] bytes = { 0, 0, 0, 25 };
// If the system architecture is little-endian (that is, little end first),
// reverse the byte array.
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
int i = BitConverter.ToInt32(bytes, 0);
Console.WriteLine("int: {0}", i);
// Output: int: 25
Example
In this example, the int
to an array of bytes.
Note
The output may differ depending on the endianess of your computer's architecture.
byte[] bytes = BitConverter.GetBytes(201805978);
Console.WriteLine("byte array: " + BitConverter.ToString(bytes));
// Output: byte array: 9A-50-07-0C
How to: Convert a String to a Number
You can convert a to a number by using methods in the class or by using the TryParse
method found on the various numeric types (int, long, float, etc.).
If you have a string, it is slightly more efficient and straightforward to call a TryParse
method (for example, int.TryParse(“11”)
). Using a Convert
method is more useful for general objects that implement .
You can use Parse
or TryParse
methods on the numeric type you expect the string contains, such as the type. The method uses internally. If the string is not in a valid format, Parse
throws an exception whereas TryParse
returns .
Example
The Parse
and TryParse
methods ignore whitespace at the beginning and at the end of the string, but all other characters must be characters that form the appropriate numeric type (int, long, ulong, float, decimal, etc.). Any whitespace within the characters that form the number cause an error. For example, you can use decimal.TryParse
to parse “10”, “10.3”, “ 10 “, but you cannot use this method to parse 10 from “10X”, “1 0” (note space), “10 .3” (note space), “10e1” (float.TryParse
works here), and so on.
The examples below demonstrate both successful and unsuccessful calls to Parse
and TryParse
.
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
int numVal = Int32.Parse("-105");
Console.WriteLine(numVal);
// Output: -105
// TryParse returns true if the conversion succeeded
// and stores the result in j.
int j;
if (Int32.TryParse("-105", out j))
Console.WriteLine(j);
else
Console.WriteLine("String could not be parsed.");
// Output: -105
try
{
int m = Int32.Parse("abc");
}
catch (FormatException e)
{
Console.WriteLine(e.Message);
}
// Output: Input string was not in a correct format.
string inputString = "abc";
int numValue;
bool parsed = Int32.TryParse(inputString, out numValue);
if (!parsed)
Console.WriteLine("Int32.TryParse could not parse '{0}' to an int.\n", inputString);
// Output: Int32.TryParse could not parse 'abc' to an int.
// This snippet shows a couple of examples that extract number characters from the
// beginning of the string to avoid TryParse errors.
StringBuilder sb = new StringBuilder();
var str = " 10FFxxx";
foreach (char c in str) {
// Check for numeric characters (hex in this case). Add "." and "e" if float,
// and remove letters. Include initial space because it is harmless.
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || c == ' ') {
sb.Append(c);
}
else
break;
}
if (int.TryParse(sb.ToString(), System.Globalization.NumberStyles.HexNumber, null, out i))
Console.WriteLine(sb.ToString());
str = " -10FFXXX";
sb.Clear();
foreach (char c in str) {
// Check for numeric characters (allow negative in this case but no hex digits).
// Though we use int.TryParse in the previous example and this one, int.TryParse does NOT
// allow a sign character (-) AND hex digits at the same time.
// Include initial space because it is harmless.
if ((c >= '0' && c <= '9') || c == ' ' || c == '-') {
sb.Append(c);
} else
break;
}
if (int.TryParse(sb.ToString(), out i))
Console.WriteLine(sb.ToString());
Example
The following table lists some of the methods from the
class that you can use.Numeric Type | Method |
---|---|
decimal | |
float | |
double | |
short | |
int | |
long | |
ushort | |
uint | |
ulong |
This example calls the
method to convert an input to an . The code catches the two most common exceptions that can be thrown by this method, and . If the number can be incremented without overflowing the integer storage location, the program adds 1 to the result and prints the output.using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
static void Main(string[] args)
{
int numVal = -1;
bool repeat = true;
while (repeat)
{
Console.WriteLine("Enter a number between −2,147,483,648 and +2,147,483,647 (inclusive).");
string input = Console.ReadLine();
// ToInt32 can throw FormatException or OverflowException.
try
{
numVal = Convert.ToInt32(input);
}
catch (FormatException e)
{
Console.WriteLine("Input string is not a sequence of digits.");
}
catch (OverflowException e)
{
Console.WriteLine("The number cannot fit in an Int32.");
}
finally
{
if (numVal < Int32.MaxValue)
{
Console.WriteLine("The new value is {0}", numVal + 1);
}
else
{
Console.WriteLine("numVal cannot be incremented beyond its current value");
}
}
Console.WriteLine("Go again? Y/N");
string go = Console.ReadLine();
if (go == "Y" || go == "y")
{
repeat = true;
}
else
{
repeat = false;
}
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
// Sample Output:
// Enter a number between -2,147,483,648 and +2,147,483,647 (inclusive).
// 473
// The new value is 474
// Go again? Y/N
// y
// Enter a number between -2,147,483,648 and +2,147,483,647 (inclusive).
// 2147483647
// numVal cannot be incremented beyond its current value
// Go again? Y/N
// Y
// Enter a number between -2,147,483,648 and +2,147,483,647 (inclusive).
// -1000
// The new value is -999
// Go again? Y/N
// n
// Press any key to exit.
How to: Convert Between Hexadecimal Strings and Numeric Types
These examples show you how to perform the following tasks:
Obtain the hexadecimal value of each character in a
.Obtain the
that corresponds to each value in a hexadecimal string.Convert a hexadecimal
string
to an .Convert a hexadecimal
string
to a .Convert a
array to a hexadecimalstring
.
Example
This example outputs the hexadecimal value of each character in a string
. First it parses the string
to an array of characters. Then it calls on each character to obtain its numeric value. Finally, it formats the number as its hexadecimal representation in a string
.
string input = "Hello World!";
char[] values = input.ToCharArray();
foreach (char letter in values)
{
// Get the integral value of the character.
int value = Convert.ToInt32(letter);
// Convert the decimal value to a hexadecimal value in string form.
string hexOutput = String.Format("{0:X}", value);
Console.WriteLine("Hexadecimal value of {0} is {1}", letter, hexOutput);
}
/* Output:
Hexadecimal value of H is 48
Hexadecimal value of e is 65
Hexadecimal value of l is 6C
Hexadecimal value of l is 6C
Hexadecimal value of o is 6F
Hexadecimal value of is 20
Hexadecimal value of W is 57
Hexadecimal value of o is 6F
Hexadecimal value of r is 72
Hexadecimal value of l is 6C
Hexadecimal value of d is 64
Hexadecimal value of ! is 21
*/
Example
This example parses a string
of hexadecimal values and outputs the character corresponding to each hexadecimal value. First it calls the method to obtain each hexadecimal value as an individual string
in an array. Then it calls to convert the hexadecimal value to a decimal value represented as an . It shows two different ways to obtain the character corresponding to that character code. The first technique uses , which returns the character corresponding to the integer argument as a string
. The second technique explicitly casts the int
to a .
string hexValues = "48 65 6C 6C 6F 20 57 6F 72 6C 64 21";
string[] hexValuesSplit = hexValues.Split(' ');
foreach (String hex in hexValuesSplit)
{
// Convert the number expressed in base-16 to an integer.
int value = Convert.ToInt32(hex, 16);
// Get the character corresponding to the integral value.
string stringValue = Char.ConvertFromUtf32(value);
char charValue = (char)value;
Console.WriteLine("hexadecimal value = {0}, int value = {1}, char value = {2} or {3}",
hex, value, stringValue, charValue);
}
/* Output:
hexadecimal value = 48, int value = 72, char value = H or H
hexadecimal value = 65, int value = 101, char value = e or e
hexadecimal value = 6C, int value = 108, char value = l or l
hexadecimal value = 6C, int value = 108, char value = l or l
hexadecimal value = 6F, int value = 111, char value = o or o
hexadecimal value = 20, int value = 32, char value = or
hexadecimal value = 57, int value = 87, char value = W or W
hexadecimal value = 6F, int value = 111, char value = o or o
hexadecimal value = 72, int value = 114, char value = r or r
hexadecimal value = 6C, int value = 108, char value = l or l
hexadecimal value = 64, int value = 100, char value = d or d
hexadecimal value = 21, int value = 33, char value = ! or !
*/
Example
This example shows another way to convert a hexadecimal string
to an integer, by calling the method.
string hexString = "8E2";
int num = Int32.Parse(hexString, System.Globalization.NumberStyles.HexNumber);
Console.WriteLine(num);
//Output: 2274
Example
The following example shows how to convert a hexadecimal string
to a by using the class and the method.
string hexString = "43480170";
uint num = uint.Parse(hexString, System.Globalization.NumberStyles.AllowHexSpecifier);
byte[] floatVals = BitConverter.GetBytes(num);
float f = BitConverter.ToSingle(floatVals, 0);
Console.WriteLine("float convert = {0}", f);
// Output: 200.0056
Example
The following example shows how to convert a
array to a hexadecimal string by using the class.byte[] vals = { 0x01, 0xAA, 0xB1, 0xDC, 0x10, 0xDD };
string str = BitConverter.ToString(vals);
Console.WriteLine(str);
str = BitConverter.ToString(vals).Replace("-", "");
Console.WriteLine(str);
/*Output:
01-AA-B1-DC-10-DD
01AAB1DC10DD
*/
'프로그래밍 > C#' 카테고리의 다른 글
C# Programming Guide - Main() and Command-Line Arguments (0) | 2017.05.10 |
---|---|
C# Programming Guide - Strings (0) | 2017.05.10 |
C# Programming Guide - Inside a C# Program (0) | 2017.05.10 |
C# Programming Guide - Statements, Expressions, and Operators (0) | 2017.05.10 |
C# Programming Guide - Arrays (0) | 2017.05.09 |