The future function of C # programming language __ Programming

Source: Internet
Author: User
Tags class definition reflection reserved microsoft c

Show TOC
Welcome to MSDN > Development languages and Tools
Future features of the C # programming language
Release Date: 4/30/2003 | Renew Date: 6/25/2004
Prashant Sridharan

Microsoft Corporation

Apply to: Microsoft®visual C # (TM)

Summary: Microsoft Corporation is developing the next major version of the C # language. This article describes four major new features: Generics, iterators, anonymous methods, and local types.

Content of this page
Brief introduction
Generic type
Iterative Program
anonymous method
Local type
Conform to the standard
Availability of
More information

Brief introduction
C # is an innovative new programming language that skillfully combines the most commonly used industry language and research language functions. While keeping the C # design in mind, Microsoft has introduced several potential new features in the C # language, increasing the developer's efficiency in language construction.

Microsoft C #
Since the advent of C # in February 2001, many developers have begun to use the C # programming language to build software. Microsoft itself also uses C # to build several formal applications, including the. NET Framework, the MSN Web properties, and the Tablet PC SDK. Thus, C # is a language suitable for constructing high quality commercial software.

Many of the features in the C # language are created based on the following four different design goals:

• Unified type systems and the use of simplified value types and reference types in the C # language.

• Build component-based design through XML annotations, attributes, attributes, events, and delegates.

• Establish practical developer control capabilities with the help of the unique features of the C # language, including safe pointer manipulation, overflow checking, and more.

• Build practical language constructs such as foreach and using statements to improve developer productivity.

In the "Visual Studio for Yukon" version of the C # language, Microsoft plans to build a concise, practical syntax by combining a wide range of research languages with various features in the industry language. These language features include generics, iterators, anonymous methods, and local types.

Potential future capabilities
In fact, the future innovation function of C # is mainly based on the unified type System, component-based development, developer control function and practical language construction. The following is a summary of the four major new features that Microsoft plans to provide in the next major version of the C # language. The design of these features has not yet been completed, and Microsoft Corporation welcomes extensive developers to comment on these features.

Back to the top of the page
Generic type
As projects become more complex, programmers increasingly need a way to better reuse and customize their existing component-based software. In order to implement the reuse of advanced code in other languages, programmers typically use a feature called generics. C # will include a safe and efficient generic that differs slightly in syntax from the generics proposed in the templates in C + + and the Java language, but there are significant differences in how they are implemented.

Generate the latest generic class
With current C #, programmers can create a finite form of true generics by storing data in an instance of the base object type. Because each object in C # is inherited from the base object type, plus the boxing and unboxing capabilities of the unified. NET type system, programmers can store reference types and value types in variables of object types. However, there are some performance flaws in the conversion between reference types, value types, and base object types.

To illustrate this, the following code example creates a simple Stack type that contains two actions "Push" and "Pop". The Stack class stores its data in an array of object types, and the Push and Pop methods use the Base object type to accept and return data:

public class Stack
{
Private object[] items = new OBJECT[100];

public void Push (object data)
{
...
}

public Object Pop ()
{
...
}
}

You can then press the custom type, such as the Customer type, onto the stack. However, if your program needs to retrieve data, you need to explicitly convert the results of the Pop method (the Base object type) to the Customer type.

Stack s = new stack ();
S.push (New Customer ());
Customer C = (customer) s.pop ();

If a value type, such as an integer, is passed to the Push method, the runtime automatically converts it to a reference type, which is called boxing, and then stores it in an internal data structure. Similarly, if a program wants to retrieve a value type from the stack, such as an integer, the object type obtained from the Pop method needs to be explicitly converted to a value type, which is called unboxing:

Stack s = new stack ();
S.push (3);
int i = (int) s.pop ();

Boxing and unboxing operations between value types and reference types are onerous.

Also, in the current implementation, the data types placed in the stack cannot be restricted. In fact, you can create a stack and then press the Customer type onto the stack. You can then use the same stack and try to pop the data, and then convert it to another type, as shown in the following example:

Stack s = new stack ();
S.push (New Customer ());

Employee E = (employee) s.pop ();

Although the previous code example mistakenly used a single type of Stack class to implement, it should be treated as an error, but it is actually a legitimate code and there is no problem compiling it. At run time, however, the program will fail due to an invalid conversion operation.

Creating and using Generics
Generics in C # can create efficient data structures that are intended for use by the compiler based on the type they are using. After these so-called parameterized types are created, their internal algorithms remain unchanged, but the types of their internal data can be changed with the end user settings.

To help developers save time learning the language, the generic declaration methods in C # are roughly the same as in C + +. Programmers can create classes and structs in the usual way and specify type parameters using angle bracket tags (< and >). When you use a class, you must replace each parameter with the actual type provided by the user of the class.

The following example creates a Stack class that specifies and declares a type parameter named ItemType in the angle brackets after the class declaration. An instance of a generic Stack class will accept the type created for it and store data for that type locally, rather than converting between the created type and the base object type. The type parameter ItemType acts as a proxy until the type is specified in the instantiation process and used as the type of the internal item array (that is, the argument type of the Push method and the return type of the Pop method):

public class Stack<itemtype>
{
Private itemtype[] items;

public void Push (ItemType data)
{
...
}

Public ItemType Pop ()
{
...
}
}

When a program uses the Stack class in the following example, you can specify the actual type that the generic class uses. This example uses the angle bracket notation in the instantiation statement to specify the original integer type as a parameter, indicating that the Stack class uses this type:

stack<int> stack = new stack<int> ();
Stack. Push (3);
int x = stack. Pop ();

When you do this, the program creates a new instance of the Stack class, where each ItemType is replaced by the supplied integer parameter. In fact, when a program creates a new instance of the Stack class with an integer argument, the project array that is stored locally inside the Stack class is an integer, not an object. The program also eliminates the boxing problem associated with putting integers on the stack. In addition, when a program pops up an item from the stack, you do not have to explicitly convert it to the appropriate type, because the current specific instance of the Stack class stores the integer locally in its data structure.

If you want your program to store other types of items in the Stack class, you must create a new instance of the Stack class and specify the new type as a parameter. Suppose you have a simple Customer type, and you want your program to store the type using a Stack object. To do this, you can easily reuse program code by simply instantiating the Stack class and using the Customer object as its type parameter:

stack<customer> stack = new stack<customer> ();
Stack. Push (New Customer ());
Customer C = Stack. Pop ();

Of course, if your program creates a stack class that takes the customer type as a parameter, you can store the customer type only on that stack. In fact, generics in C # have strict types, which means that you cannot store integers in the stack, as shown in the following example:

stack<customer> stack = new stack<customer> ();
Stack. Push (New Customer ());
Stack. Push (3)//compile-time error
Customer C = Stack.      Pop (); Type conversions are not required.

Advantages of generics
With generics, programmers can reuse the code for a variety of different data types by writing, testing, and deploying the code once. The first stack example has this feature, and the second stack example allows a program to reuse code that has little impact on its application performance. For value types, the first stack example has a large performance problem, and the second stack example completely eliminates the problem because it removes boxing and downward type conversions.

Also, generics are checked at compile-time. When a program instantiates a generic class with a supplied type parameter, this type parameter can only be the type that the program specifies in the class definition. For example, if a program creates a stack of a Customer object type, the integer cannot be pressed onto the stack. By enforcing this action, you can generate more reliable code.

In addition, the C # Implementation of generics reduces the speed of code expansion compared to other stringent type implementations. Creating a collection of types with generics allows you to avoid creating specific variants of each class while maintaining the operational performance advantage. For example, a program can create a parameterized Stack class without creating integerstack for storing integers, stringstack for storing strings, and customerstack for storing Customer types.

This can increase the readability of your code. By simply creating a stack class, the program can encapsulate all operations associated with a stack in a convenient class. Then, when creating the customer type stack, although the customer type is stored, it is obvious that the program is still using the stack data structure.

Multiple type parameters

Generics can use any number of parameter types. Only one type is used in the Stack example above. Suppose you create a simple Dictionary class that stores values and keys. A generic version of the Dictionary class can be defined in a program by declaring two arguments (placed in the angle brackets of the class definition and separated by commas):

public class Dictionary<keytype, valtype>
{
public void Add (KeyType key, Valtype val)
{
...
}

Public Valtype This[keytype Key]
{
...
}
}

When using the Dictionary class, you need to provide multiple comma-delimited arguments in the angle brackets of the instantiated statement and provide the correct type of parameters for the ADD function and the index Builder:

Dictionary<int, customer> dict = new Dictionary<int, customer> ();
Dict. ADD (3, New Customer ());
Customer C = Dict. GET[3];

Constraints

Typically, a program is not limited to storing data based on a given type parameter, but often requires members of the type parameter to execute statements in the program's generics.

Why do I need constraints

Suppose in the Add method of the Dictionary class, you need to compare the project using the CompareTo method of the provided key, for example:

public class Dictionary<keytype, valtype>
{
public void Add (KeyType key, Valtype val)
{
...
Switch (Key.compareto (x))
{
}
...
}
}

Unfortunately, as expected, type parameter KeyType is generic at compile time. If you write this, the compiler assumes that a key instance that is KeyType to a type parameter can only perform operations that apply to the Base object type (for example, ToString). As a result, the compiler will display a compilation error because the CompareTo method is undefined. However, a program can convert a key variable to an object that contains a CompareTo method, such as a IComparable interface. In the following example, the program explicitly converts an instance key of the KeyType parameter type to a IComparable interface that the program can compile:

public class Dictionary<keytype, valtype>
{
public void Add (KeyType key, Valtype val)
{
...
Switch ((IComparable) key). CompareTo (x))
{
}
...
}
}

However, if the Dictionary class is immediately instantiated and the supplied type parameter does not implement the IComparable interface, the program encounters a run-time error, especially a InvalidCastException exception.

declaring constraints

In C #, a program can provide a list of optional constraints for each type parameter declared in a generic class. Constraints represent the requirements that must be met to construct a type as a generic. You can use the WHERE keyword to declare a constraint, followed by a parameter-requirement pair, where "parameter" must be a parameter defined in the generic, and "requirement" must be a class or interface.

To satisfy the need to use the CompareTo method in the Dictionary class, the program can add constraints to the KeyType type parameter, requiring that any type passed to the Dictionary class as the first parameter must implement the IComparable interface, for example:

public class Dictionary<keytype, valtype> where keytype:icomparable
{
public void Add (KeyType key, Valtype val)
{
...
Switch (Key.compareto (x))
{
}
...
}
}

This way, the code is checked when the code is compiled to ensure that the type passed as the first parameter implements the IComparable interface each time the program uses the Dictionary class. In addition, the program does not have to explicitly convert the variable to the IComparable interface until the CompareTo method is invoked.

Multiple constraints

For any given type parameter, the program can specify any number of interface constraints for it, but only one class constraint at a maximum. Each new constraint is declared in the form of another "parameter-requirement" pair, and each constraint for a given generic is separated by commas. The Dictionary class in the following example contains two parameters, KeyType and Valtype. The KeyType type parameter has two interface constraints, and the Valtype type parameter has a class constraint:

public class Dictionary<keytype, valtype> where
Keytype:icomparable,
Keytype:ienumerable,
Valtype:customer
{
public void Add (KeyType key, Valtype val)
{
...
Switch (Key.compareto (x))
{
}
...
}
}

Generics at run time

There is almost no difference between the compilation method of a generic class and the normal class. In fact, the result of compilation is simply metadata and intermediate language (IL). Of course, the IL should be parameterized in order to accept the type provided by the user in the code. The usage of the IL of a generic is different depending on whether the supplied type parameter is a value type or a reference type.

When you first construct a generic with a value type as a parameter, the runtime replaces the corresponding location in the IL with the supplied arguments to create a specialized generic. For each unique value type used as a parameter, a dedicated generic is created at once.

For example, suppose the program code declares a Stack constructed of integers:

Stack<int> Stack;

At this point, the runtime generates a dedicated Stack class and replaces the corresponding arguments for the class with an integer. Now, regardless of when the program code uses an integer stack, the runtime will reuse the generated private stack class. The following example creates two instances of the integer stack, each of which is created using the code generated by the runtime of this integer stack:

stack<int> Stackone = new stack<int> ();
stack<int> stacktwo = new stack<int> ();

However, if you create a Stack class elsewhere in your program code, and uses different value types (such as long or user-defined structures) as their arguments, the runtime generates other forms of generics, which then replaces the long integer parameters in the corresponding IL position. The advantage of creating a specialized class for generics constructed with value types is that better performance can be achieved. After all, each dedicated generic class contains a value type in "local", so no more conversions are necessary.

Generics work in a slightly different way from reference types. When you first construct a generic with any reference type, the runtime replaces the arguments in the IL with an object reference to create a specialized generic. After that, whenever a constructed type is instantiated using a reference type as a parameter, regardless of the type being constructed, the runtime repeats the private generics created earlier.

For example, suppose you have two reference types, the customer class, and the Order class, and further assume that you created the customer type Stack:

Stack<customer> customers;

At this point, the runtime generates a private Stack class that does not store data, but instead stores the object references that are subsequently populated. Suppose the next line of code creates a Stack of other reference types, called Order:

stack<order> orders = new stack<order> ();

Unlike a value type, there is no other specialized stack class created for the order type, but instead creates an instance of the private stack class and sets the orders variable to reference it. For each object reference that replaces the type parameter, allocates the memory space according to the size of the order type and sets the pointer to reference the memory location. Suppose you subsequently encountered a line of code to create a Stack of the Customer type:

Customers = new stack<customer> ();

As with the previous stack class created with the order type, another instance of the private Stack class is created and the pointers contained in it are set to memory regions that reference the size of the Customer type. Because different programs have significant differences in the number of reference types, the C # Implementation of generics greatly reduces the rate of code expansion by reducing the number of reference types to the number of specialized classes created by the compiler for generic classes of reference types.

In addition, when you instantiate a generic C # class with a type parameter, whether it is a value type or a reference type, you can query with reflection and actual types at run time, and you can determine its type parameters.

Differences between C # generics and other implementations

There is a significant difference between C + + templates and C # generics. C # generics are compiled into IL, which makes it intelligently to create the appropriate specialized type for each value type at run time, but only once for a reference type; C + + templates are actually code extension macros that generate a specialized type for each type parameter provided to the template. Therefore, when the C + + compiler encounters a template (such as an integer stack), it expands the template code into a stack class and includes the integer as the type of the class itself. Whether a type parameter is a value type or a reference type, if you do not specifically design a linker to reduce code bloat, the C + + compiler creates a specialized class each time, resulting in a more significant code expansion than using C # generics.

Also, C + + templates cannot define constraints. C + + templates implicitly define constraints only by using one member, which may or may not be part of the type parameter. If the member is eventually passed to the type parameter of the generic class, the program will run correctly. Otherwise, the program will fail and may return a hidden error message. Because C # generics can declare constraints and have strict types, these potential errors do not exist.

Now, Sun microsystems® has added additional generics in the new version of the Java language (code name "Tiger"). Sun-selected implementations do not need to modify the Java virtual machine. Therefore, Sun is faced with the problem of how to implement generics on unmodified virtual machines.

The proposed Java implementation uses similar syntax, including type parameters and constraints, with templates in C + + and generics in C #. However, because it handles value types differently from handling reference types, the unmodified Java virtual machine does not support generics of value types. As a result, generics in Java cannot be executed effectively. In fact, the Java compiler inserts an automatic downward type conversion from the specified constraint (if declared) or the Base object type (if no constraints are declared) when the data needs to be returned. In addition, the Java compiler will generate a private type at run time and then use it to instantiate any constructed type. Finally, because the Java virtual machine itself does not support generics, it is not possible to determine the type parameters of a generic instance at run time, and other uses of reflection are severely restricted.

Generics support in other languages
Microsoft's goal is to support the use and creation of generics in Visual J # (TM), Visual C + +, and visual Basic. Although the time to implement this feature is early in different languages, all the other three languages of Microsoft will include support for generics. At the same time, the C # team is working on adding the appropriate functionality to the basic runtime of generics, laying the groundwork for multi-language support. Microsoft works closely with third party language partners to ensure that generics are created and used in a. NET based language.

Back to the top of the page
Iterative Program
An iterator is a language that is built on the study of similar functions in languages, such as CLU, Sather, and Icon. Simply put, a type can easily declare the way a foreach statement iterates its elements through an iterator.

Why do I need an iterator

Now, if the class needs to support iterative operations using the Foreach loop structure, they must implement the enumerator pattern. For example, the compiler extends the Foreach loop structure on the left to the while loop structure on the right:

List List = ...;
foreach (object obj in list)
{
DoSomething (obj);
}


Enumerator e = list. GetEnumerator ();
while (E.movenext ())
{
Object obj = e.current;
DoSomething (obj);


It is noteworthy that in order for the Foreach loop to work correctly, the List data structure (an instance of the iteration) must support the GetEnumerator function. After you create the List data structure, you must implement the GetEnumerator function to return the Listenumerator object:

public class List
{
Internal object[] elements;
internal int count;

Public Listenumerator GetEnumerator ()
{
Return to New Listenumerator (this);
}
}

The Listenumerator object you create must not only implement the current property and the MoveNext method, but also maintain its internal state so that the program can move to the next item each time the loop is executed. This internal state machine is simpler for the List data structure, but for data structures (such as binary trees) that require a recursive loop, the state machine will be quite complex.

Because implementing this enumerator pattern requires the developer to devote a lot of effort and write a lot of code, C # contains a new structure that makes it easy for a class to indicate how the Foreach loop iterates over its content.

Defining an iterator

Because an iterator is a logical counterpart to a Foreach loop structure, it is defined like a function: Use the foreach keyword followed by a pair of parentheses. In the following example, the program declares an iterator for the List type. The return type of the iterator is determined by the user, but because the List class stores the object type internally, the return type of the following iterator sample is an object:

public class List
{
Internal object[] elements;
internal int count;

public Object foreach ()
{
}
}

It is noteworthy that after implementing the enumerator pattern, the program needs to maintain an internal state machine to track the location of the program in the data structure. An iterator has a built-in state machine. With the new yield keyword, a program can return a value to a foreach statement that invokes the iterator. When the foreach statement next loops and calls the iterator again, the iterator begins execution at the point where the last yield statement stopped. In the following example, the program generates three string types:

public class List
{
Internal object[] elements;
internal int count;

public string foreach ()
{
Yield "Microsoft";
Yield "Corporation";
Yield "Developer Division";
}
}

In the following example, the Foreach loop that calls this iterator executes three times, each time receiving a string in the order specified by the first three yield statements:

List List = new List ();
foreach (string s in list)
{
Console.WriteLine (s);
}

If you want the program to implement an iterator to traverse the elements in the list, you need to use a Foreach loop to modify this iterator to traverse the array of elements and produce each of the items in the array in each iteration:

public class List
{
Internal object[] elements;
internal int count;

public Object foreach ()
{
foreach (object o in elements)
{
Yield o;
}
}
}

How the iterative process works

The iterator represents the program that handles the day-to-day operation of implementing the enumerator pattern. The C # compiler converts the code that you write in an iterator into the corresponding class and code that uses the enumerator pattern without having to create a class and build a state machine. In this way, the iterative program significantly improves the productivity of the developer.

Back to the top of the page
anonymous method
An anonymous method is another practical language structure that enables programmers to create blocks of code that can be boxed in a delegate and executed later. They are based on language concepts called lambda functions and are similar to the corresponding language concepts in Lisp and Python.

Create delegate Code

A delegate is an object that refers to a method. When a delegate is invoked, the method it refers to is invoked. The following example illustrates a simple form that contains a list box, text box, and button three controls. When the button is initialized, the program instructs its Click delegate to refer to the AddClick method that is stored elsewhere in the object. In the AddClick method, the value of the text box is stored in the list box. Because the AddClick method is added to the click Delegate of the button instance, this method is called each time the button is clicked.

public class MyForm
{
ListBox listbox;
TextBox textbox;
Button button;

Public MyForm ()
{
ListBox = new ListBox (...);
TextBox = new TextBox (...);
button = New button (...);
button. Click + + new EventHandler (AddClick);
}

void AddClick (object sender, EventArgs e)
{
LISTBOX.ITEMS.ADD (TextBox.Text);
}
}

Using anonymous Methods

The previous example is very intuitive. A separate function is created, and a delegate reference is made to it, which is called by the program whenever this delegate is invoked. In this function, a series of executable steps are performed. With an anonymous method, a program does not need to create an entire new method for the class, but can directly reference the executable steps contained in the delegate. The method of declaring an anonymous method is to instantiate a delegate, then add a pair of curly braces that represent the execution scope after instantiating the statement, plus a semicolon to terminate the statement.

In the following example, the program modifies the delegate creation statement to modify the list box directly, rather than referencing the function that represents the program to modify the list box. The purpose of storing the code is to modify the list box in the execution scope after the delegate creation statement.

public class MyForm
{
ListBox listbox;
TextBox textbox;
Button button;

Public MyForm ()
{
ListBox = new ListBox (...);
TextBox = new TextBox (...);
button = New button (...);
button. Click + + new EventHandler (sender, E)
{
LISTBOX.ITEMS.ADD (TextBox.Text);
};
}
}

Note that the code in the anonymous method is how to access and process variables declared outside the scope of its execution. In practice, anonymous methods can refer to variables declared by classes and parameters, or to local variables declared by the method in which they reside.

Passing parameters to anonymous methods

Interestingly, the anonymous method statement contains two parameters, namely sender and E. Looking at the definition of the Click delegate for the Button class, you will find that any function referenced by the delegate must contain two parameters, the first parameter is the object type, and the second parameter is the EventArgs type. In the first example, the program did not use the anonymous method, but instead passed two parameters to the AddClick method, the type being object and EventArgs.

Even if this code is written inline, the delegate must still receive two parameters. In the anonymous method, you must declare the names of two parameters so that the associated code block can use them. When the Click event on the button is triggered, the anonymous method is invoked and the corresponding argument is passed to the method.

How the anonymous method works

When an anonymous delegate is encountered, the C # compiler automatically converts code within its execution scope to a unique named function in a unique named class. The delegate that stores the block of code is then set up to reference the compiler-generated objects and methods. When a delegate is invoked, the "anonymous" method block is executed through the compiler-generated method.

Back to the top of the page
Local type
Although it is a good way for object-oriented programming to maintain all the source code for a type in a single file, sometimes performance constraints make the type larger. In addition, the overhead of splitting a type into a subtype in some cases is unacceptable. Also, programmers often create or use applications to publish source code and modify the result code. Unfortunately, when the source code is released again, all existing source-code modifications will be overwritten.

Local types allow you to split types that contain a large number of source code into multiple different source files for easy development and maintenance. In addition, a local type can be used to separate the part of a computer-generated type from the user-written type, making it easier to supplement or modify the code generated by the tool.

In the following example, classes named Foo are defined in the two C # code files File1.cs and File2.cs. If you do not use a local type, a compilation error occurs because the two classes exist in the same namespace. Using the partial keyword, you can instruct the compiler that other definitions of this class may be included elsewhere.

File1.cs File2.cs
public partial class Foo
{
public void MyFunction ()
{
Perform actions here
}
}


public partial class Foo
{
public void Myotherfunction ()
{
Perform actions here
}
}

At compile time, the C # compiler collects all the definitions of the local type and groups them together. The result IL generated by the compiler shows a single class that is grouped together, rather than being continuously displayed as a separate class for each class.

Back to the top of the page
Conform to the standard
In December 2001, the European Computer Manufacturers Association (ECMA) approved the C # programming language as a standard (ECMA 334). Shortly thereafter, the C # standard was quickly tracked by the International Organization for Standardization (ISO) and was expected to be approved soon. The creation of the C # Standard is an important milestone in the development of the new programming language, and it indicates that many implementations are expected to be written on various operating system platforms in the future. In fact, as we can see from its brief history, many Third-party compiler vendors and researchers have implemented it as a standard and created their own version of the C # compiler.

Microsoft welcomes customers ' feedback on adding the features mentioned above in the C # language, and intends to submit these features to the ongoing language standardization process.

Back to the top of the page
Availability of
The features described below will be implemented in a future version of the C # compiler. In early 2003, the "Everett" version of Visual Studio. NET will contain a version of C # that has been slightly modified to meet the ECMA standard. This version does not contain the features described in this article. Microsoft intends to include these features in the "VS for Yukon" version of Visual Studio, but the specific release date has not been determined.

In the next few months, Microsoft publishes detailed information about these features, including all specifications. Programmers and language design groups are welcome to present their own views and feedback on these features as well as any other language features of interest. You can send e-mail to mailto:sharp@microsoft.com and get in touch with the C # language designer.

Back to the top of the page
More information
C # Community Web site: http://www.csharp.net

Visual C # (tm) Product Web site: http://msdn.microsoft.com/vcsharp


Back to the top of the page

Personal Information Center | MSDN Chinese Express Mail | Contact us
©2005 Microsoft Corporation.  All rights reserved. All rights reserved | trademark | Privacy Statement

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.