Interviewer of Famous Enterprises: Part C of typical programming questions
C #
C # is a programming language that Microsoft simultaneously releases when launching a new development platform. net. Since windows is still the most operating system. net is a development platform launched by Microsoft in recent years. Therefore, C # is widely used in desktop software and network application development, therefore, it is not difficult to understand why many companies developed based on Windows systems require candidates to master C #.
C # can be seen as a managed language developed based on C ++. Therefore, many of its keywords and even syntaxes are similar to C ++. A programmer who has learned C ++ programming can use C # To develop software in less time. However, we should also be aware that although it is easy to learn the same or similar parts of C # And C ++, however, it is not easy to master and distinguish between the two. Interviewers always like to go deep into our ambiguity to check whether we really understand it. Therefore, we should pay attention to the different syntax features of c # And C ++. The following interview is an example:
Interviewer: In C ++, you can use struct and class to define the type. What are the differences between the two types?
Applicant: If the access permission level of member functions or member variables is not specified, the default access permission level in struct is public, and the default access permission level in class is private.
Interviewer: What about C?
Applicant: C # is different from C ++. In C #, if the access permission level of the member function or member variable is not indicated, both struct and class are private. The difference between struct and class is that struct defines the value type, and the value type instance allocates memory on the stack; while the class defines the reference type, and the reference type instance allocates memory on the stack.
In C #, each type has a constructor like C ++. However, unlike C ++, we can define a finalizer and dispose method for the type in C # to release resources. Although the finalizer method looks the same as the C ++ destructor, it is followed by the type name, but the call time of the finalizer method is different from that of the C ++ destructor, the finalizer of C # is called only when the CLR is used for garbage collection. Its call time is determined by the runtime. Therefore, it is uncertain for programmers. In addition, in C #, you can define a special constructor for the type: static constructor. This function is automatically called at runtime before the type is used for the first time, and is only called once. There are many interesting questions about static constructors, such as running the following C # code. What are the output results?
Class
{
Public A (string text)
{
Console. writeline (text );
}
}
Class B
{
Static A a1 = new A ("A1 ");
Aa2 = new A ("A2 ");
Static B ()
{
A1 = new A ("A3 ");
}
Public B ()
{
A2 = new A ("A4 ");
}
}
Class Program
{
Static void main (string [] ARGs)
{
B = new B ();
}
}
Execute the static constructor of B before calling the code of type B. The static constructor initializes the type of static variables before executing the statements in the function. Therefore, print A1 first and then A3. Next, execute B = new B (), that is, call the normal constructor of B. The constructor first initializes the member variables and then executes the statements in the function body. Therefore, A2 and A4 are printed successively. Therefore, run the above Code and print out four lines, namely A1, A3, A2, and A4.
In addition to the different knowledge points of C # And C ++, we also need to pay special attention to some special functions of C #, such as reflection and appdomain. These concepts are also correlated and it takes a lot of time to study and thoroughly understand them. The following code is a piece of code about reflection and application domains. What is the result of running it?
[Serializable]
Internal Class A: marshalbyrefobject
{
Public static int number;
Public void setnumber (INT value)
{
Number = value;
}
}
[Serializable]
Internal Class B
{
Public static int number;
Public void setnumber (INT value)
{
Number = value;
}
}
Class Program
{
Static void main (string [] ARGs)
{
String assambly = assembly. getentryassembly (). fullname;
Appdomain domain = appdomain. createdomain ("newdomain ");
A. Number = 10;
String nameofa = typeof (a). fullname;
A A = domain. createinstanceandunwrap (assambly, nameofa) as;
A. setnumber (20 );
Console. writeline ("number in Class A is {0}", A. number );
B. Number = 10;
String nameofb = typeof (B). fullname;
B = domain. createinstanceandunwrap (assambly, nameofb) as B;
B. setnumber (20 );
Console. writeline ("number in Class B is {0}", B. number );
}
}
The above C # code first creates an application domain named newdomain, and uses the reflection mechanism in this domain to create an instance of type A and an instance of type B. We noticed that type A inherits from marshalbyrefobject, while Type B does not. Although the two types have the same structure, the behavior displayed when crossing the boundaries of the application domain varies greatly due to different base classes.
Consider the case of a first. Because a inherits from marshalbyrefobject, A actually only acts as a proxy in the default domain and points to an instance of a in the newdomain domain. When setnumber of method A is called, this method is called in the newdomain domain. It modifies the value of static variable A. number in the newdomain domain and sets it to 20. Because static variables have an independent copy in each application domain, modifying the static variable A. number in the newdomain domain has no effect on the static variable A. number in the default domain. Because console. writeline Outputs A. number in the default application domain, the output is still 10.
Next we will discuss B. Because B only inherits the type from the object, when its instance crosses the boundary of the application domain, it will completely copy the instance. Therefore, in the above Code, although we try to generate B's instance in the newdomain domain, we will copy instance B to the default application domain. In this case, Method B. setnumber is also called on the default application domain. It modifies a. number on the default domain and sets it to 20. When console. writeline is called on the default domain, It outputs 20.
We recommend two books related to C # below to help you deal with C # interview and learn C # well #.
Profession1c #. The biggest feature of this book is that there are several chapters in the appendix dedicated to programmers who already have experience in other languages (such as VB, C ++ and Java, it details the differences between C # and other languages. After reading these chapters, it will not confuse C # With the previous languages.
Jeffreyrichter's CLR via C #. This book not only deeply introduces the C # language, but also comprehensively analyzes Clr and. net. If we can read this book, we will be able to deeply understand the concepts of packing, unloading, garbage collection, reflection, and so on, it is naturally not difficult to pass the C # related interview.
Interview Question 2: Singleton Mode
Question: To design a class, we can only generate one instance of the class.
A class that can only generate one instance is a type that implements the singleton (Singleton) mode. Because the design pattern plays an important role in Object-Oriented Programming, many companies like to ask questions related to the design pattern during the interview process. In the common mode, Singleton is the only mode that can be fully implemented with dozens of lines of code. Therefore, writing a singleton is a common interview question.
Bad solution 1: Only applicable to single-threaded Environments
Because only one instance can be generated, we must set the constructor as a private function to prevent others from creating instances. We can define a static instance and create it as needed. The following definition type singleton1 is implemented based on this idea:
Public sealed class singleton1
{
Private singleton1 ()
{
}
Private Static singleton1 instance = NULL;
Public static singleton1 instance
{
Get
{
If (instance = NULL)
Instance = new singleton1 ();
Return instance;
}
}
}
In the static attribute instance of Singleton, the code above creates an instance only when the instance is null to avoid repeated creation. At the same time, we define the constructor as a private function to ensure that only one instance is created.
Solution 2: although it can work in a multi-threaded environment, it is not efficient.
The code in solution 1 works normally in a single thread, but there is a problem in the case of multithreading. Suppose that if the two threads run the if statement to determine whether the instance is null and the instance is not created, both threads will create an instance, in this case, singleton1 does not meet the requirements of singleton1. To ensure that only one instance of the type can be obtained in a multi-threaded environment, a synchronization lock is required. Modify singleton1 to get the following code:
Public sealed class singleton2
{
Private singleton2 ()
{
}
Private Static readonly object syncobj = new object ();
Private Static singleton2 instance = NULL;
Public static singleton2 instance
{
Get
{
Lock (syncobj)
{
If (instance = NULL)
Instance = newsingleton2 ();
}
Return instance;
}
}
}
We still assume that two threads want to create an instance at the same time. Because only one thread can get the synchronization lock at a time, when the first thread is locked, the second thread can only wait. When the first thread finds that the instance has not been created, it creates an instance. Next, the first thread releases the synchronization lock. At this time, the second thread can add the synchronization lock and run the following code. At this time, because the instance has been created by the first thread, the second thread will not create instances again, so that we can only get one instance in the multi-threaded environment.
However, singleton2 is not perfect. Every time we get the singleton2 instance through the attribute instance, we will try to add a synchronization lock, and locking is a very time-consuming operation. We should avoid it whenever necessary.
Feasible Solution: Determine whether the instance already exists twice before and after the synchronization lock is applied
We only need to lock the instance before it is created to ensure that only one thread creates the instance. When the instance has been created, we no longer need to lock it. So we can further improve the Code in solution 2:
Public sealed class singleton3
{
Private singleton3 ()
{
}
Private Static object syncobj = new object ();
Private Static singleton3 instance = NULL;
Public static singleton3 instance
{
Get
{
If (instance = NULL)
{
Lock (syncobj)
{
If (instance = NULL)
Instance = newsingleton3 ();
}
}
Return instance;
}
}
}
In singleton3, the lock operation is required only when the instance is null and is not created. When the instance has been created, no locks are required. Because the instance is null at the first time, you only need to lock the instance when trying to create the instance for the first time. In this way, singleton3 has a much better time efficiency than singleton2.
Singleton3 uses the locking mechanism to ensure that only one instance is created in a multi-threaded environment, and uses two if statements to improve efficiency. Such code implementation is complicated and error-prone, and we have better solutions.
Strongly recommended solution 1: using static Constructor
The C # syntax includes a function that can be called only once, that is, the static constructor. We can use the C # feature to implement the singleton mode as follows:
Public sealed class singleton4
{
Private singleton4 ()
{
}
Private Static singleton4 instance = new singleton4 ();
Public static singleton4 instance
{
Get
{
Return instance;
}
}
}
The implementation code of singleton4 is very concise. We create an instance when initializing the static variable instance. Since C # initializes static variables when calling static constructors, the. NET runtime can ensure that only one static constructor is called, so that we can ensure that only one instance is initialized.
In C #, the time to call a static constructor is not controlled by the programmer, but is automatically called when. Net runs and finds that a type is used for the first time. Therefore, in singleton4, the instance is not created when the singleton4.instance attribute is called for the first time, but is created when singleton4 is used for the first time. Assume that we add a static method in singleton4 and call this static function without creating an instance. However, if singleton4 is used to implement the singleton4 mode, it will still create instances too early to reduce the memory usage efficiency.
Strongly recommended solution 2: Create instances as needed
The last Implementation of singleton5 solves the problem of premature instance creation in singleton4:
Public sealed class singleton5
{
Singleton5 ()
{
}
Public static singleton5 instance
{
Get
{
Return nested. instance;
}
}
Class nested
{
Static nested ()
{
}
Internal static readonly singleton5 instance = new singleton5 ();
}
}
In the singleton5 code above, a private type nested is defined internally. When this nested type is used for the first time, the static constructor is called to create an instance of singleton5. The Type nested is only used in the singleton5.instance attribute. Because of its private attributes, others cannot use the nested type. Therefore, when we try to obtain the singleton5 instance through the singleton5.instance attribute for the first time, the nested static constructor will be automatically called to create an instance. If we do not call the singleton5.instance attribute, it will not trigger the. NET runtime to call nested, nor create an instance, so that we can create on demand.
Solution comparison
In the first five ways to implement the singleton mode, the first method does not work normally in a multi-threaded environment, while the second mode can work normally in a multi-threaded environment, but the time efficiency is very low, they are not what the interviewer expects. In the third method, we determine the lock twice to ensure efficient operation in a multi-threaded environment. The fourth method utilizes the static constructor feature of C # To ensure that only one instance is created. The fifth method utilizes the characteristics of private nested types to create instances only when they are actually needed, improving the space usage efficiency. If the fourth or fifth solution is provided during the interview, it will undoubtedly be favored by the interviewer.
Source code:
For the complete source code of this question, see the 02_singleton project.
Exam point:
Measure the test taker's knowledge about Singleton mode.
Measure the test taker's knowledge about the basic syntax of C #, such as static constructor.
Measure the test taker's understanding about multithreaded programming.
Extension of this question:
In the previous code, the implementation of the five Singleton modes marked the types as sealed, indicating that they cannot be used as the base classes of other types. Now we need to define a type President that represents the President, from which we can inherit the types such as frenchpresident and americanpresident. All these derived types can only generate one instance. How can we design and implement these types?
This article is from the book "offoffoffer-famous enterprise interviewer excellent typical programming questions"
Book details: http://blog.csdn.net/broadview2006/article/details/7043805