Const: We cannot modify the value later in the code. Value assigning is mandatory to constant variables.
Readonly: Value can be changed during runtime and can be assigned in the constructor or at the time of declaration.
“out” parameter can be passed to a method and it need not be initialized where as
“ref” parameter has to be initialized before it is used.
What is the difference between “StringBuilder” and “String” in C#?
- StringBuilder is mutable, which means once object for stringbuilder
is created, it later be modified either using Append, Remove or Replace.
- String is immutable and it means we cannot modify the string object and will always create new object in memory of string type.
Generics is a concept to declare type parameters which makes it possible to design class and methods in such a way so that we can hold the type specification until class or method is declared and instantiated by client code without incurring the cost of runtime cast or boxing.
What is Nullable Types in C#?
Variable types does not hold null values so to hold the null values
we have to use nullable types. So nullable types can have values either
null or other values as well.
Eg: Int? mynullablevar = null;
Why to use “Nullable Coalescing Operator” (??) in C#?
Nullable Coalescing Operator can be used with reference types and nullable value types. So if the first operand of the expression is null then the value of second operand is assigned to the variable. For example,
double? myFirstno = null;
double mySecno;
mySecno = myFirstno ?? 10.11;
What is enum in C#?
enum keyword is used for declaring an enumeration, which consists of named constants and it is called as enumerator lists. Enums are value types in C# and these can’t be inherited. Below is the sample code of using Enums
Eg: enum Fruits { Apple, Orange, Banana, WaterMelon};
Explain Partial Class in C#?
Partial classes concept added in .Net Framework 2.0 and it allows us to split the business logic in multiple files with the same class name along with “partial” keyword.
Explain the types of unit test cases?
Below are the list of unit test case types –
Explain Indexers in C#?double mySecno;
mySecno = myFirstno ?? 10.11;
What is enum in C#?
enum keyword is used for declaring an enumeration, which consists of named constants and it is called as enumerator lists. Enums are value types in C# and these can’t be inherited. Below is the sample code of using Enums
Eg: enum Fruits { Apple, Orange, Banana, WaterMelon};
Explain Partial Class in C#?
Partial classes concept added in .Net Framework 2.0 and it allows us to split the business logic in multiple files with the same class name along with “partial” keyword.
Explain the types of unit test cases?
Below are the list of unit test case types –
- Positive Test cases
- Negative Test cases
- Exception Test cases
Indexers are a syntactic convenience that enable you to create a class, struct, or interface that client applications can access just as an array. Indexers are most frequently implemented in types whose primary purpose is to encapsulate an internal collection or array
class TempRecord { // Array of temperature values private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F, 61.3F, 65.9F, 62.1F, 59.2F, 57.5F }; // To enable client code to validate input // when accessing your indexer. public int Length { get { return temps.Length; } } // Indexer declaration. // If index is out of range, the temps array will throw the exception. public float this[int index] { get { return temps[index]; } set { temps[index] = value; } } } class MainClass { static void Main() { TempRecord tempRecord = new TempRecord(); // Use the indexer's set accessor tempRecord[3] = 58.3F; tempRecord[5] = 60.1F; // Use the indexer's get accessor for (int i = 0; i < 10; i++) { System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]); } // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } /* Output: Element #0 = 56.2 Element #1 = 56.7 Element #2 = 56.5 Element #3 = 58.3 Element #4 = 58.8 Element #5 = 60.1 Element #6 = 65.9 Element #7 = 62.1 Element #8 = 59.2 Element #9 = 57.5 */
Attributes in C#?
- Attributes are meant to add additional information about a class,
function, etc. Compilers sometimes do special things when they see an
attribute. Other attributes are looked at by libraries while the program
is running.
- Attributes can only be applied before the code is compiled.
- Custom attribute is an attribute you create yourself as opposed to one that came with .NET.
What is Thread in C#?
Thread is an execution path of a program
Different states of a Thread
Explain the methods and properties of Thread class in C#?
Below are the methods and properties of thread class –
- CurrentCulture
- CurrentThread
- CurrentContext
- IsAlive
- IsThreadPoolThread
- IsBackground
- Priority
Class: A class is the generic definition of what an object is. A Class describes all the attributes of the object, as well as the methods that implement the behavior of the member object. In other words, class is a template of an object
Object: An object is physical representation of a class. An object is an instance of a class
What are the Access Modifiers in C# ?
Different Access Modifier are - Public, Private, Protected, Internal, Protected Internal
- Public – When a method or attribute is defined as Public, it can be accessed from any code in the project. For example, in the above Class “Employee” getName() and setName() are public.
- Private - When a method or attribute is defined as Private, It can
be accessed by any code within the containing class only. For example,
in the above Class “Employee” attributes name and salary
can be accessed within the Class Employee Only. If an attribute or
class is defined without access modifiers, it's default access modifier
will be private.
- Protected - When attribute and methods
are defined as protected, it can be accessed by any method in the
inherited classes and any method within the same class. The protected
access modifier cannot be applied to classes and interfaces. Methods and fields in a interface can't be declared protected.
- Internal – If an attribute or method is defined as Internal, access
is restricted to classes within the current project assembly.
- Protected Internal – If an attribute or method is defined as
Protected Internal, access is restricted to classes within the current
project assembly and types derived from the containing class.
Properties are a type of class member, that are exposed to the outside world
What is Operator Overloading?
C# allows user-defined types to overload operators by defining static member functions using the operator keyword. Not all operators can be overloaded.
class Rectangle
{ private int Height; private int Width; public Rectangle(int w,int h) { Width=w; Height=h; } public static bool operator >(Rectangle a,Rectangle b) { return a.Height > b.Height ; } public static bool operator <(Rectangle a,Rectangle b) { return a.Height < b.Height ; } }
public static void Main() { Rectangle obj1 = new Rectangle(200,400); Rectangle obj2 = new Rectangle(100,200); if(obj1 > obj2) { Console.WriteLine("Rectangle1 is greater than Rectangle2"); } if(obj1 < obj2) { Console.WriteLine("Rectangle1 is less than Rectangle2"); } }
Dispose vs Finalize
Finalize
- Finalizers should always be
protected
, notpublic
orprivate
so that the method cannot be called from the application's code directly and at the same time, it can make a call to thebase.Finalize
method - Finalizers should release unmanaged resources only.
- The framework does not guarantee that a finalizer will execute at all on any given instance.
- Never allocate memory in finalizers or call virtual methods from finalizers.
- Avoid synchronization and raising unhandled exceptions in the finalizers.
- The execution order of finalizers is non-deterministic—in other words, you can't rely on another object still being available within your finalizer.
- Do not define finalizers on value types.
- Don't create empty destructors. In other words, you should never explicitly define a destructor unless your class needs to clean up unmanaged resources and if you do define one, it should do some work. If, later, you no longer need to clean up unmanaged resources in the destructor, remove it altogether.
Dispose
- Implement
IDisposable
on every type that has a finalizer - Ensure that an object is made unusable after making a call to the
Dispose
method. In other words, avoid using an object after theDispose
method has been called on it. - Call
Dispose
on allIDisposable
types once you are done with them - Allow
Dispose
to be called multiple times without raising errors. - Suppress later calls to the finalizer from within the
Dispose
method using theGC.SuppressFinalize
method - Avoid creating disposable value types
- Avoid throwing exceptions from within
Dispose
methods
Dispose/Finalized Pattern
- Microsoft recommends that you implement both
Dispose
andFinalize
when working with unmanaged resources. TheFinalize
implementation would run and the resources would still be released when the object is garbage collected even if a developer neglected to call theDispose
method explicitly. - Cleanup the unmanaged resources in the
Finalize
method as well asDispose
method. Additionally call theDispose
method for any .NET objects that you have as components inside that class(having unmanaged resources as their member) from theDispose
method.
What is Delegate?
Explain Reflection?
ViewData vs ViewBag vs TempData
Boxing vs Marshalling vs Serialization
Boxing: convert a value type to reference type (stack to heap) e.g int to string
Marshalling: process to send data across program boundaries e.g from managed to unmanaged code
Serialization: is the conversion of object type to binary/text so that it can be transferred over the network or stored on disk
7. What is Virtualization?
Base classes may define and implement virtual methods, and derived classes can override them, which means they provide their own definition and implementation. At run-time, when client code calls the method, the CLR looks up the run-time type of the object, and invokes that override of the virtual method. Thus in your source code you can call a method on a base class, and cause a derived class's version of the method to be executed.
Hiding Base Class Members with New Members
If you want your derived member to have the same name as a member in a base class, but you do not want it to participate in virtual invocation, you can use the new keyword. The
new
keyword is put before the return type of a class member that is being replacedKnowing When to Use Override and New Keywords
In C#, a method in a derived class can have the same name as a method in the base class. You can specify how the methods interact by using the new and override keywords. The
override
modifier extends the base class method, and the new
modifier hides it.Versioning with the Override and New Keywords
The C# language is designed so that versioning between base and derived classes in different libraries can evolve and maintain backward compatibility. This means, for example, that the introduction of a new member in a base class with the same name as a member in a derived class is completely supported by C# and does not lead to unexpected behavior. It also means that a class must explicitly state whether a method is intended to override an inherited method, or whether a method is a new method that hides a similarly named inherited method.
In C#, derived classes can contain methods with the same name as base class methods.
- The base class method must be defined virtual.
- If the method in the derived class is preceded with the
new
keyword, the method is defined as being independent of the method in the base class. - If the method in the derived class is preceded with the
override
keyword, objects of the derived class will call that method instead of the base class method. - The base class method can be called from within the derived class using the
base
keyword. - The
override
,virtual
, andnew
keywords can also be applied to properties, indexers, and events.
By default, C# methods are not virtual. If a method is declared as virtual, any class inheriting the method can implement its own version. To make a method virtual, the
virtual
modifier is used in the method declaration of the base class. The derived class can then override the base virtual method by using the override
keyword or hide the virtual method in the base class by using the new
keyword. If neither the override
keyword nor the new
keyword is specified, the compiler will issue a warning and the method in the derived class will hide the method in the base class.
To demonstrate this in practice, assume for a moment that Company A has created a class named
GraphicsClass
, which your program uses. The following is GraphicsClass
:class GraphicsClass { public virtual void DrawLine() { } public virtual void DrawPoint() { } }
Your company uses this class, and you use it to derive your own class, adding a new method:
class YourDerivedGraphicsClass : GraphicsClass { public void DrawRectangle() { } }
Your application is used without problems, until Company A releases a new version of
GraphicsClass
, which resembles the following code:class GraphicsClass { public virtual void DrawLine() { } public virtual void DrawPoint() { } public virtual void DrawRectangle() { } }
The new version of
GraphicsClass
now contains a method named DrawRectangle
. Initially, nothing occurs. The new version is still binary compatible with the old version. Any software that you have deployed will continue to work, even if the new class is installed on those computer systems. Any existing calls to the method DrawRectangle
will continue to reference your version, in your derived class.
However, as soon as you recompile your application by using the new version of
GraphicsClass
, you will receive a warning from the compiler, CS0108. This warning informs you that you have to consider how you want your DrawRectangle
method to behave in your application.
If you want your method to override the new base class method, use the
override
keyword:class YourDerivedGraphicsClass : GraphicsClass { public override void DrawRectangle() { } }
Theoverride
keyword makes sure that any objects derived fromYourDerivedGraphicsClass
will use the derived class version ofDrawRectangle
. Objects derived fromYourDerivedGraphicsClass
can still access the base class version ofDrawRectangle
by using the base keyword:
base.DrawRectangle();
If you do not want your method to override the new base class method, the following considerations apply. To avoid confusion between the two methods, you can rename your method. This can be time-consuming and error-prone, and just not practical in some cases. However, if your project is relatively small, you can use Visual Studio's Refactoring options to rename the method. For more information, see Refactoring Classes and Types (Class Designer).
Alternatively, you can prevent the warning by using the keyword
new
in your derived class definition:class YourDerivedGraphicsClass : GraphicsClass { public new void DrawRectangle() { } }
Using the new
keyword tells the compiler that your definition hides the definition that is contained in the base class. This is the default behavior.
When a method is named on a class, the C# compiler selects the best method to call if more than one method is compatible with the call, such as when there are two methods with the same name, and parameters that are compatible with the parameter passed. The following methods would be compatible:
public class Derived : Base { public override void DoWork(int param) { } public void DoWork(double param) { } }
When
DoWork
is called on an instance of Derived
, the C# compiler will first try to make the call compatible with the versions of DoWork
declared originally on Derived
. Override methods are not considered as declared on a class, they are new implementations of a method declared on a base class. Only if the C# compiler cannot match the method call to an original method on Derived
will it try to match the call to an overridden method with the same name and compatible parameters. For example:int val = 5; Derived d = new Derived(); d.DoWork(val); // Calls DoWork(double).
Because the variable
val
can be converted to a double implicitly, the C# compiler calls DoWork(double)
instead of DoWork(int)
. There are two ways to avoid this. First, avoid declaring new methods with the same name as virtual methods. Second, you can instruct the C# compiler to call the virtual method by making it search the base class method list by casting the instance of Derived
to Base
. Because the method is virtual, the implementation of DoWork(int)
on Derived
will be called. For example: ((Base)d).DoWork(val); // Calls DoWork(int) on Derived.
When a class is declared as sealed it cannot be inherited. Abstract classes cannot be declared sealed
9. Difference between Abstract Class and Static Class?
An abstract class is usually intended to model something in a type hierarchy. For example, a truck is a kind of vehicle, and an airplane is a kind of vehicle, so you might have a base class Vehicle and derived classes Truck and Airplane. But "Vehicle" is abstract; there are no vehicles which are just vehicles without being some more specific kind of thing. You represent that concept with an abstract class.
A static class by contrast is not intended to model anything at all. It's just a convenient way of storing a bunch of code. Really it shouldn't be a class at all; VB made a better choice by calling such things "modules" rather than "classes". Though technically they inherit from object, static classes are logically not really in a type hierarchy at all. They're just a bucket for holding static members. Static classes are often used as containers of extension methods.
When do I use what and why?
Use an abstract class when you want to build a model of the form "an X is a kind of Y". Like "a Car is a kind of Vehicle" or "a Square is a kind of Shape" or "a Magazine is a kind of Publication", where the "Y" is an abstract concept. Don't use it for things like "an Employee is a kind of Person" -- Person should be concrete. Person is not an abstract concept; there are people who are just people, but there are no vehicles that are not something else.
Use a static class when you want to make extension methods, or when you have a bunch of code that fits logically together but does not associate with any object. For example, if you have a bunch of related math routines, that's a good candidate for a static class.
Static classes actually do not really exist as a concept in the CLR. When you say "static" on a class, what we actually do is generate an abstract sealed class with no public constructors. Since it is abstract, you cannot create one directly. Since it is sealed, you cannot create a more derived class and instantiate that.
What are the distinct characteristics of an abstract class?
- You cannot instantiate an abstract class directly. This implies that you cannot create an object of the abstract class; it must be inherited.
- You can have abstract as well as non-abstract members in an abstract class.
- You must declare at least one abstract method in the abstract class.
- An abstract class is always public.
- An abstract class is declared using the abstract keyword.
The basic purpose of an abstract class is to provide a common definition of the base class that multiple derived classes can share.
When do you really need to create an abstract class?
We define abstract classes when we define a template that needs to be followed by all the derived classes.
Differentiate between an abstract class and an interface.
Abstract Class:
- A class can extend only one abstract class
- The members of abstract class can be private as well as protected
- Abstract classes should have subclasses
- Any class can extend an abstract class
- Methods in abstract class can be abstract as well as concrete
- There can be a constructor for abstract class
- The class extending the abstract class may or may not implement any of its method
- An abstract class can implement methods
Interface
- A class can implement several interfaces
- An interface can only have public members
- Interfaces must have implementations by classes
- Only an interface can extend another interface
- All methods in an interface should be abstract
- Interface does not have constructor
- All methods of interface need to be implemented by a class implementing that interface
- Interfaces cannot contain body of any of its method
What is static constructor?
A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed once only. It is called automatically before the first instance is created or any static members are referenced.
Static constructors have the following properties:
- A static constructor does not take access modifiers or have parameters.
- A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.
- A static constructor cannot be called directly.
- The user has no control on when the static constructor is executed in the program.
- A typical use of static constructors is when the class is using a log file and the constructor is used to write entries to this file.
- Static constructors are also useful when creating wrapper classes for unmanaged code, when the constructor can call the
LoadLibrary
method. - If
a static constructor throws an exception, the runtime will not invoke
it a second time, and the type will remain uninitialized for the
lifetime of the application domain in which your program is running.
public class Bus { // Static variable used by all Bus instances. // Represents the time the first bus of the day starts its route. protected static readonly DateTime globalStartTime; // Property for the number of each bus. protected int RouteNumber { get; set; } // Static constructor to initialize the static variable. // It is invoked before the first instance constructor is run. static Bus() { globalStartTime = DateTime.Now; // The following statement produces the first line of output, // and the line occurs only once. Console.WriteLine("Static constructor sets global start time to {0}", globalStartTime.ToLongTimeString()); } // Instance constructor. public Bus(int routeNum) { RouteNumber = routeNum; Console.WriteLine("Bus #{0} is created.", RouteNumber); } // Instance method. public void Drive() { TimeSpan elapsedTime = DateTime.Now - globalStartTime; // For demonstration purposes we treat milliseconds as minutes to simulate // actual bus times. Do not do this in your actual bus schedule program! Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.", this.RouteNumber, elapsedTime.TotalMilliseconds, globalStartTime.ToShortTimeString()); } } class TestBus { static void Main() { // The creation of this instance activates the static constructor. Bus bus1 = new Bus(71); // Create a second bus. Bus bus2 = new Bus(72); // Send bus1 on its way. bus1.Drive(); // Wait for bus2 to warm up. System.Threading.Thread.Sleep(25); // Send bus2 on its way. bus2.Drive(); // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } /* Sample output: Static constructor sets global start time to 3:57:08 PM. Bus #71 is created. Bus #72 is created. 71 is starting its route 6.00 minutes after global start time 3:57 PM. 72 is starting its route 31.00 minutes after global start time 3:57 PM. */
What is Private Constructor?
A private constructor is a special instance constructor. It is generally used in classes that contain static members only. If a class has one or more private constructors and no public constructors, other classes (except nested classes) cannot create instances of this class
The declaration of the empty constructor prevents the automatic generation of a default constructor. Note that if you do not use an access modifier with the constructor it will still be private by default. However, the private modifier is usually used explicitly to make it clear that the class cannot be instantiated.
Private constructors are used to prevent creating instances of a class when there are no instance fields or methods, such as the Math class, or when a method is called to obtain an instance of a class. If all the methods in the class are static, consider making the complete class static
You can use it to force a singleton instance or create a factory class
static
method within the class which creates an instance, or returns a reference to an existing instance.
Generally, they are used in
singleton
design patterns, where the code ensures that only one instance of a class can ever be created.public class Foo
{
private Foo (){}
private Foo FooInstance {get;set;}
public static Foo GetFooInstance ()
{
if(FooInstance == null){
FooInstance = new Foo();
}
return FooInstance;
}
}
This allows only one instance of the class to be created. 10. What is Singleton Pattern?
The singleton pattern is one of the best-known patterns in software engineering. Essentially, a singleton is a class which only allows a single instance of itself to be created, and usually gives simple access to that instance. Most commonly, singletons don't allow any parameters to be specified when creating the instance - as otherwise a second request for an instance but with a different parameter could be problematic! (If the same instance should be accessed for all requests with the same parameter, the factory pattern is more appropriate.) This article deals only with the situation where no parameters are required. Typically a requirement of singletons is that they are created lazily - i.e. that the instance isn't created until it is first needed.
Four common characteristics,
- A single constructor, which is private and parameterless. This prevents other classes from instantiating it (which would be a violation of the pattern). Note that it also prevents subclassing - if a singleton can be subclassed once, it can be subclassed twice, and if each of those subclasses can create an instance, the pattern is violated. The factory pattern can be used if you need a single instance of a base type, but the exact type isn't known until runtime.
- The class is sealed. This is unnecessary, strictly speaking, due to the above point, but may help the JIT to optimise things more.
- A static variable which holds a reference to the single created instance, if any.
-
A public static means of getting the reference to the single created instance, creating
one if necessary.
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
You need to have a method like
getInstance()
inside your singleton and this method should ensure there's only once instance12. What is Dynamic?
The dynamic keyword is new to C# 4.0, and is used to tell the compiler that a variable's type can change or that it is not known until runtime. Think of it as being able to interact with an Object without having to cast it.
Explain Abstraction
1. Abstraction is one of the principle of object oriented programming. It is used to display only necessary and essential features of an object to ouside the world.Means displaying what is necessary and encapsulate the unnecessary things to outside the world.Hiding can be achieved by using "private" access modifiers.
2. Abstraction means to show only the necessary details to the client of the object
public abstract class Car
{
public abstract string Name();
public abstract string Color();
public abstract string Engine();
}
public class Honda : Car
{
public override string Name()
{
return "Civic";
}
public override string Color()
{
return "White";
}
public override string Engine()
{
return "1800cc";
}
}
Encapsulation vs Abstraction
Encapsulation is -
- Hiding Complexity,
- Binding Data and Function together,
- Making Complicated Method's Private,
- Making Instance Variable's Private,
- Hiding Unnecessary Data and Functions from End User.
Encapsulation implements Abstraction.
And Abstraction is -
- Showing Whats Necessary,
- Data needs to abstract from End User,
13. What is Polymorphism?
1. Polymorphism is the ability for an object to change its behaviour according to how it is being used. Where an object's class inherits from a super-class or implements one or more interfaces, it can be referred to by those class or interface names. So if we have a method that expects an object of type 'vehicle' to be passed as a parameter, we can pass any vehicle, car or motorcycle object to that method even though the data type may be technically different.
2. Polymorphism means one name many forms. Polymorphism means one object behaving as multiple forms. One function behaves in different forms. In other words, "Many forms of a single object is called Polymorphism."
Person behaves as a SON in house, at the same time that person behaves like an EMPLOYEE in the office.
Static or Compile Time Polymorphism
Dynamic or Runtime Polymorphism
Compiler demands virtual
Show()
method and it compiles successfully. The right version of Show()
method cannot be determined until run-time since only at that time Base objBase
is initialized as Derived
.13. Difference between Var (Anonymous type) and Dynamic ?
Background
Variables declared with
var
are implicitly but statically typed. Variables declared with dynamic
are dynamically typed. This capability was added to the CLR in order to support dynamic languages like Ruby and Python.This means that
dynamic
declarations are resolved at run-time, var
declarations are resolved at compile-time.Table of difference
var | dynamic |
Introduced in C# 3.0 | Introduced in C# 4.0 |
Statically typed – This means the type of variable declared is decided by the compiler at compile time. | Dynamically typed - This means the type of variable declared is decided by the compiler at runtime time. |
Need to initialize at the time of declaration. e.g., var str=”I am a string”; Looking at the value assigned to the variable str , the compiler will treat the variable str as string. | No need to initialize at the time of declaration. e.g., dynamic str; str=”I am a string”; //Works fine and compilesstr=2; //Works fine and compiles |
Errors are caught at compile time. Since the compiler knows about the type and the methods and properties of the type at the compile time itself | Errors are caught at runtime Since the compiler comes to about the type and the methods and properties of the type at the run time. |
Visual Studio shows intellisense since the type of variable assigned is known to compiler. | Intellisense is not available since the type and its related methods and properties can be known at run time only |
e.g., var obj1; will throw a compile error since the variable is not initialized. The compiler needs that this variable should be initialized so that it can infer a type from the value. | e.g., dynamic obj1; will compile; |
e.g. var obj1=1; will compile var obj1=” I am a string”;
will throw error since the compiler has already
decided that the type of obj1 is System.Int32 when the value 1 was
assigned to it. Now assigning a string value to it violates the type
safety. | e.g. dynamic obj1=1; will compile and run dynamic obj1=” I am a string”; will compile and run since the compiler creates the type for obj1 as System.Int32 and then recreates the type as string when the value “I am a string” was assigned to it. This code will work fine. |
No comments:
Post a Comment