Showing posts with label C# Charts. Show all posts
Showing posts with label C# Charts. Show all posts

Monday, August 10, 2009

C# Interview Questions

C# Interview Questions
====================

This is a list of questions I have gathered from other sources and created myself over a period of time from my experience, many of which I felt where incomplete or simply wrong. I have finally taken the time to go through each question and correct them to the best of my ability. However, please feel free to post feedback to challenge, improve, or suggest new questions. I want to thank those of you that have contributed quality questions and corrections thus far.

There are some question in this list that I do not consider to be good questions for an interview. However, they do exist on other lists available on the Internet so I felt compelled to keep them easy access.


General Questions

1. Does C# support multiple-inheritance?
No.

2. Who is a protected class-level variable available to?
It is available to any sub-class (a class inheriting this class).

3. Are private class-level variables inherited?
Yes, but they are not accessible. Although they are not visible or accessible via the class interface, they are inherited.


4. Describe the accessibility modifier “protected internal”.
It is available to classes that are within the same assembly and derived from the specified base class.

5. What’s the top .NET class that everything is derived from?
System.Object.

6. What does the term immutable mean?
The data value may not be changed. Note: The variable value may be changed, but the original immutable data value was discarded and a new data value was created in memory.

7. What’s the difference between System.String and System.Text.StringBuilder classes?
System.String is immutable. System.StringBuilder was designed with the purpose of having a mutable string where a variety of operations can be performed.

8. What’s the advantage of using System.Text.StringBuilder over System.String?
StringBuilder is more efficient in cases where there is a large amount of string manipulation. Strings are immutable, so each time a string is changed, a new instance in memory is created.

9. Can you store multiple data types in System.Array?
No.

10. What’s the difference between the System.Array.CopyTo() and System.Array.Clone()?
The Clone() method returns a new array (a shallow copy) object containing all the elements in the original array. The CopyTo() method copies the elements into another existing array. Both perform a shallow copy. A shallow copy means the contents (each array element) contains references to the same object as the elements in the original array. A deep copy (which neither of these methods performs) would create a new instance of each element's object, resulting in a different, yet identacle object.


11. How can you sort the elements of the array in descending order?
By calling Sort() and then Reverse() methods.

12. What’s the .NET collection class that allows an element to be accessed using a unique key?
HashTable.

13. What class is underneath the SortedList class?
A sorted HashTable.

14. Will the finally block get executed if an exception has not occurred?­
Yes.

15. What’s the C# syntax to catch any possible exception?
A catch block that catches the exception of type System.Exception. You can also omit the parameter data type in this case and just write catch {}.


16. Can multiple catch blocks be executed for a single try statement?
No. Once the proper catch block processed, control is transferred to the finally block (if there are any).

17. Explain the three services model commonly know as a three-tier application.
Presentation (UI), Business (logic and underlying code) and Data (from storage or other sources).


Class Questions


1. What is the syntax to inherit from a class in C#?
Place a colon and then the name of the base class.
Example: class MyNewClass : MyBaseClass


2. Can you prevent your class from being inherited by another class?
Yes. The keyword “sealed” will prevent the class from being inherited.

3. Can you allow a class to be inherited, but prevent the method from being over-ridden?
Yes. Just leave the class public and make the method sealed.

4. What’s an abstract class?
A class that cannot be instantiated. An abstract class is a class that must be inherited and have the methods overridden. An abstract class is essentially a blueprint for a class without any implementation.

5. When do you absolutely have to declare a class as abstract?
1. When the class itself is inherited from an abstract class, but not all base abstract methods have been overridden.
2. When at least one of the methods in the class is abstract.


6. What is an interface class?
Interfaces, like classes, define a set of properties, methods, and events. But unlike classes, interfaces do not provide implementation. They are implemented by classes, and defined as separate entities from classes.

7. Why can’t you specify the accessibility modifier for methods inside the interface?
They all must be public, and are therefore public by default.

8. Can you inherit multiple interfaces?
Yes. .NET does support multiple interfaces.

9. What happens if you inherit multiple interfaces and they have conflicting method names?
It’s up to you to implement the method inside your own class, so implementation is left entirely up to you. This might cause a problem on a higher-level scale if similarly named methods from different interfaces expect different data, but as far as compiler cares you’re okay.
To Do: Investigate


10. What’s the difference between an interface and abstract class?
In an interface class, all methods are abstract - there is no implementation. In an abstract class some methods can be concrete. In an interface class, no accessibility modifiers are allowed. An abstract class may have accessibility modifiers.

11. What is the difference between a Struct and a Class?
Structs are value-type variables and are thus saved on the stack, additional overhead but faster retrieval. Another difference is that structs cannot inherit.



Method and Property Questions


1. What’s the implicit name of the parameter that gets passed into the set method/property of a class?
Value. The data type of the value parameter is defined by whatever data type the property is declared as.

2. What does the keyword “virtual” declare for a method or property?
The method or property can be overridden.

3. How is method overriding different from method overloading?
When overriding a method, you change the behavior of the method for the derived class. Overloading a method simply involves having another method with the same name within the class.


4. Can you declare an override method to be static if the original method is not static?
No. The signature of the virtual method must remain the same. (Note: Only the keyword virtual is changed to keyword override)

5. What are the different ways a method can be overloaded?
Different parameter data types, different number of parameters, different order of parameters.

6. If a base class has a number of overloaded constructors, and an inheriting class has a number of overloaded constructors; can you enforce a call from an inherited constructor to a specific base constructor?
Yes, just place a colon, and then keyword base (parameter list to invoke the appropriate constructor) in the overloaded constructor definition inside the inherited class.


Events and Delegates



1. What’s a delegate?
A delegate object encapsulates a reference to a method.

2. What’s a multicast delegate?
A delegate that has multiple handlers assigned to it. Each assigned handler (method) is called.


XML Documentation Questions


1. Is XML case-sensitive?
Yes.


2. What’s the difference between // comments, /* */ comments and /// comments?
Single-line comments, multi-line comments, and XML documentation comments.

3. How do you generate documentation from the C# file commented properly with a command-line compiler?
Compile it with the /doc switch.


Debugging and Testing Questions


1. What debugging tools come with the .NET SDK?
1. CorDBG – command-line debugger. To use CorDbg, you must compile the original C# file using the /debug switch.
2. DbgCLR – graphic debugger. Visual Studio .NET uses the DbgCLR.


2. What does assert() method do?
In debug compilation, assert takes in a Boolean condition as a parameter, and shows the error dialog if the condition is false. The program proceeds without any interruption if the condition is true.

3. What’s the difference between the Debug class and Trace class?
Documentation looks the same. Use Debug class for debug builds, use Trace class for both debug and release builds.

4. Why are there five tracing levels in System.Diagnostics.TraceSwitcher?
The tracing dumps can be quite verbose. For applications that are constantly running you run the risk of overloading the machine and the hard drive. Five levels range from None to Verbose, allowing you to fine-tune the tracing activities.


5. Where is the output of TextWriterTraceListener redirected?
To the Console or a text file depending on the parameter passed to the constructor.

6. How do you debug an ASP.NET Web application?
Attach the aspnet_wp.exe process to the DbgClr debugger.

7. What are three test cases you should go through in unit testing?
1. Positive test cases (correct data, correct output).
2. Negative test cases (broken or missing data, proper handling).
3. Exception test cases (exceptions are thrown and caught properly).


8. Can you change the value of a variable while debugging a C# application?
Yes. If you are debugging via Visual Studio.NET, just go to Immediate window.


ADO.NET and Database Questions


1. What is the role of the DataReader class in ADO.NET connections?
It returns a read-only, forward-only rowset from the data source. A DataReader provides fast access when a forward-only sequential read is needed.



2. What are advantages and disadvantages of Microsoft-provided data provider classes in ADO.NET?
SQLServer.NET data provider is high-speed and robust, but requires SQL Server license purchased from Microsoft. OLE-DB.NET is universal for accessing other sources, like Oracle, DB2, Microsoft Access and Informix. OLE-DB.NET is a .NET layer on top of the OLE layer, so it’s not as fastest and efficient as SqlServer.NET.

3. What is the wildcard character in SQL?
Let’s say you want to query database with LIKE for all employees whose name starts with La. The wildcard character is %, the proper query with LIKE would involve ‘La%’.

4. Explain ACID rule of thumb for transactions.
A transaction must be:
1. Atomic - it is one unit of work and does not dependent on previous and following transactions.
2. Consistent - data is either committed or roll back, no “in-between” case where something has been updated and something hasn’t.
3. Isolated - no transaction sees the intermediate results of the current transaction).
4. Durable - the values persist if the data had been committed even if the system crashes right after.


5. What connections does Microsoft SQL Server support?
Windows Authentication (via Active Directory) and SQL Server authentication (via Microsoft SQL Server username and password).

6. Between Windows Authentication and SQL Server Authentication, which one is trusted and which one is untrusted?
Windows Authentication is trusted because the username and password are checked with the Active Directory, the SQL Server authentication is untrusted, since SQL Server is the only verifier participating in the transaction.

7. What does the Initial Catalog parameter define in the connection string?
The database name to connect to.

8. What does the Dispose method do with the connection object?
Deletes it from the memory.
To Do: answer better. The current answer is not entirely correct.


9. What is a pre-requisite for connection pooling?
Multiple processes must agree that they will share the same connection, where every parameter is the same, including the security settings. The connection string must be identical.


Assembly Questions


1. How is the DLL Hell problem solved in .NET?
Assembly versioning allows the application to specify not only the library it needs to run (which was available under Win32), but also the version of the assembly.

2. What are the ways to deploy an assembly?
An MSI installer, a CAB archive, and XCOPY command.


3. What is a satellite assembly?
When you write a multilingual or multi-cultural application in .NET, and want to distribute the core application separately from the localized modules, the localized assemblies that modify the core application are called satellite assemblies.

4. What namespaces are necessary to create a localized application?
System.Globalization and System.Resources.

5. What is the smallest unit of execution in .NET?
an Assembly.

6. When should you call the garbage collector in .NET?
As a good rule, you should not call the garbage collector. However, you could call the garbage collector when you are done using a large object (or set of objects) to force the garbage collector to dispose of those very large objects from memory. However, this is usually not a good practice.


7. How do you convert a value-type to a reference-type?
Use Boxing.



8.What happens in memory when you Box and Unbox a value-type?
Boxing converts a value-type to a reference-type, thus storing the object on the heap. Unboxing converts a reference-type to a value-type, thus storing the value on the stack.

9.What is CAS ?
CAS(Code Access Secutiry)
CAS is the part of the .NET security model that determines whether or not a piece of code is allowed to run, and what resources it can use when it is running. For example, it is CAS that will prevent a .NET web applet from formatting your hard disk. How does CAS work? The CAS security policy revolves around two key concepts - code groups and permissions. Each .NET assembly is a member of a particular code group, and each code group is granted the permissions specified in a named permission set. For example, using the default security policy, a control downloaded from a web site belongs to the 'Zone - Internet' code group, which adheres to the permissions defined by the 'Internet' named permission set. (Naturally the 'Internet' named permission set represents a very restrictive range of permissions.)


10. What is Satellite Assemblies ?
Satellite assemblies are often used to deploy language-specific resources for an application. These language-specific assemblies work in side-by-side execution because the application has a separate product ID for each language and installs satellite assemblies in a language-specific subdirectory for each language. When uninstalling, the application removes only the satellite assemblies associated with a given language and .NET Framework version. No core .NET Framework files are removed unless the last language for that .NET Framework version is being removed. For example, English and Japanese editions of the .NET Framework version 1.1 share the same core files. The Japanese .NET Framework version 1.1 adds satellite assemblies with localized resources in a \ja subdirectory. An application that supports the .NET Framework version 1.1, regardless of its language, always uses the same core runtime files.


11. Automatic Memory Management ?
Automatic Memory Management: From a programmer's perspective, this is probably the single biggest benefit of the .NET Framework. No, I'm not kidding. Every project I've worked on in my long career of DOS and Windows development has suffered at some point from memory management issues. Proper memory management is hard. Even very good programmers have difficulty with it. It's entirely too easy for a small mistake to cause a program to chew up memory and crash, sometimes bringing the operating system to a screeching halt in the process.

Programmers understand that they're responsible for releasing any memory that they allocate, but they're not very good at actually doing it. In addition, functions that allocate memory as a side effect abound in the Windows API and in the C runtime library. It's nearly impossible for a programmer to know all of the rules. Even when the programmer follows the rules, a small memory leak in a support library can cause big problems if called enough.

The .NET Framework solves the memory management problems by implementing a garbage collector that can keep track of allocated memory references and release the memory when it is no longer referenced. A large part of what makes this possible is the blazing speed of today's processors. When you're running a 2 GHz machine, it's easy to spare a few cycles for memory management. Not that the garbage collector takes a huge number of cycles--it's incredibly efficient.
The garbage collector isn't perfect and it doesn't solve the problem of mis-managing other scarce resources (file handles, for example), but it relieves programmers from having to worry about a huge source of bugs that trips almost everybody up in other programming environments.
On balance, automatic memory management is a huge win in almost every situation.


12.What Language familar to CLR?
Any language that can be compiled into Microsoft Intermediate Language (MSIL) is considered a .NET-compliant language. Following are a few of the popular .NET-compliant languages supported by CLR: APL,COBOL,Component Pascal,Eiffel,Fortran,Haskell,JScript,Mercury,Oberon,Pascal,Perl,Python,Smalltalk,Visual Basic,Visual C#,Visual C++

Line Chart in .net

You see line charts almost all the time. If you administer a web site, your log software probably uses a few charts to display your site's traffic. If you are into stock trading, charts are used extensively to display a stock's fluctuation over a period of time. There are numerous other examples of charts used everyday, but have you ever thought about how these charts are created?

Before answering that question for you, and to make up for my extremely boring intro paragraph, let me first show you the ASP.net chart this tutorial will help you deconstruct:



Don't let the static image trick you. Refresh this page or open the chart in a new window and keep refreshing that page. Notice that the chart looks different each time the page is loaded!

Since the above example was written entirely in code, in this tutorial I will first devote some time explaining how to approach designing a line chart before delving into how the code corresponds to our design.

Designing this Chart
There are two main features I focused on when designing this chart:

1. Being able to easily resize and adjust the chart size.
2. Allowing the chart to automatically adjust to wide ranges and quantities of data.

There are other features also, of course, but I will focus my attention on these two because I think they are most tricky to nail down properly in code. In the next page, I will elaborate on the above two features are designed.

Adjusting Chart Size
An important feature I mentioned earlier is the ability to easily adjust the chart size. When I say easily adjust the chart size, I am referring not to users being able to drag and resize the chart on the fly, but you as the developer being able to resize it in the code.
This is more tricky than it seems, for resizing your chart area should appropriately resize all of the columns, scale the plotted values appropriately, etc. The following image should provide you a brief overview of the various constraints most charts have placed on them:

As you can tell by the above image, your total chart area is only a smaller part of the total area available to it. The reason is because you want some room left over for the various labels, titles, etc. Because our .NET code generates an image, you also cannot have anything displayed outside of the image boundaries. Therefore, the extra space is all that you can use for displaying any information from this aspx file.
In the code, as you will see later on, in order to customize your chart's size, all you need to do is change the appropriate values for the four offsets as well as the total image width and height. The rest is taken care of by our code logic.

Accepting a Wide Range of Values
The chart you design should easily adapt to values outside of an acceptable range. For example, if you had to vertically plot 10,000 and then 10 afterwards, it wouldn't be feasible to have a chart that was at least 10,000 pixels high. Likewise, you wouldn't want your 10 value to be plotted vertically near your 10,000 value.
Your chart range should be both realistic as well as constrained by your chart height and width. To complicate things further, you may have many values that need to be plotted, or you may only have a few values that need to be plotted. Your chart should adapt to that variation in number of data points also, for the width of each column between two plotted values depends both on the number of data points being plotted as well as your chart's width.
To top things off, you have to deal with pixel values. For example, in some cases your column widths would need to be 5.3 pixels to ensure that all data points are plotted with the last data point hugging the right edge of the chart area. With a pixel value, your column widths would only be, using the above example, only 5 pixels wide. That means at the end, there will be some unused space associated with the .3 pixels being ignored. Multiply a loss of .3 pixels by each data point, and you are talking about real pixels leading to unnecessary gaps!

Looking at the Code

Now that you have a brief idea of what to expect with designing a chart, let's go look at the code that takes our design overview and turns into something usable. The following is the full code used for drawing the chart:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

public partial class Chart_Default : System.Web.UI.Page
{
Graphics gfx;
Bitmap bmp;

protected void Page_Load(object sender, EventArgs e)
{
bmp = new Bitmap(400, 300);
gfx = Graphics.FromImage(bmp);
gfx.Clear(Color.White);
gfx.SmoothingMode = SmoothingMode.AntiAlias;

// Define Points
//int[] p = new int[] { 10, 12, 48, 102, 290, 15, 100, 25, 300, 200, 150, 200, 60, 40, 250 };

int[] p = generateRandomValues();

int[] k = new int[p.Length];

Array.Copy(p, k, p.Length);
Array.Sort(k);

DrawChart(p, k[k.Length - 1], k[0]);
}
private void DrawChart(int[] points, int maxValue, int minValue)
{
//Offset (Margin) Values
int bottomOffset = 50;
int topOffset = 30;
int leftOffset = 60;
int rightOffset = 10;

// Taking care of some bookwork (declaring/initializing variables)
int maxDataPoints = points.Length;
int chartHeight = bmp.Height - bottomOffset;
int chartWidth = bmp.Width - rightOffset;

// Adjustable Values
double adjustedMax = maxValue * .10 + maxValue;
double adjustedMin = minValue - .50 * minValue;
double adjustVerticalRatio = (chartHeight-topOffset) / adjustedMax;
double adjustHorizontalRatio = ((chartWidth - leftOffset) / (maxDataPoints - 1));

Pen chartPen = new Pen(Color.Orange, 3);
Pen gridLine = new Pen(Color.LightGray, 1);

int minYpos = chartHeight - topOffset;
int maxYpos = 10;

// Drawing the Lines
for (int i = 0; i < maxDataPoints - 1; i++)
{
int xPos = Convert.ToInt32(i * adjustHorizontalRatio) + leftOffset;
int xPos2 = Convert.ToInt32((i + 1) * adjustHorizontalRatio) + leftOffset;

int yPos = Convert.ToInt32(chartHeight - adjustVerticalRatio * points[i]);
int yPos2 = Convert.ToInt32(chartHeight - adjustVerticalRatio * points[i + 1]);

if (points[i] == minValue)
{
minYpos = yPos;
}

if (points[i] == maxValue)
{
maxYpos = yPos;
}

gfx.DrawLine(gridLine, new Point(xPos2, topOffset), new Point(xPos2, chartHeight));
gfx.DrawLine(chartPen, new Point(xPos, yPos), new Point(xPos2, yPos2));

gfx.DrawString(i.ToString(), new Font("Arial", 8), new SolidBrush(Color.Gray), new Point(xPos - 4, chartHeight + 10));
}

//Draw Border Lines
Pen borderLine = new Pen(Color.DarkGray, 2);

//Left Border
gfx.DrawLine(borderLine, new Point(leftOffset, chartHeight), new Point(leftOffset, topOffset));

//Bottom Border
gfx.DrawLine(borderLine, new Point(leftOffset, chartHeight), new Point(chartWidth, chartHeight));

//Right Border
gfx.DrawLine(borderLine, new Point(chartWidth, chartHeight), new Point(chartWidth, topOffset));

//Top Border
gfx.DrawLine(borderLine, new Point(leftOffset, topOffset), new Point(chartWidth, topOffset));

//Drawing Vertical Min/Max Values
gfx.DrawString(maxValue.ToString(), new Font("Arial", 8), new SolidBrush(Color.Gray), new Point(leftOffset - 25, maxYpos));
gfx.DrawString(minValue.ToString(), new Font("Arial", 8), new SolidBrush(Color.Gray), new Point(leftOffset - 25, minYpos));
gfx.DrawLine(gridLine, new Point(leftOffset - 25, minYpos), new Point(chartWidth, minYpos));
gfx.DrawLine(gridLine, new Point(leftOffset - 25, maxYpos), new Point(chartWidth, maxYpos));

// Title
gfx.DrawString("[ Plotting Random Numbers ]", new Font("Arial", 10, FontStyle.Bold), new SolidBrush(Color.FromArgb(0, 102, 204)), new Point(leftOffset + 60, topOffset-30));

//Finalizing and Cleaning Up
Response.ContentType = "image/jpeg";
bmp.Save(Response.OutputStream, ImageFormat.Jpeg);
bmp.Dispose();
gfx.Dispose();
Response.End();
}

private int[] generateRandomValues()
{
Random rand = new Random();
int numValues = rand.Next(10, 20);

int[] newArray = new int[numValues];

for (int i = 0; i < numValues; i++)
{
newArray[i] = rand.Next(100, 1000);
}
return newArray;
}
}

I will not be going through every line of code like I normally do. There are two reasons for that:
1.The code itself is fairly simple. I sacrificed small-picture things for the big-picture result. In non-MBA speak, that means I only coded the important features. Features that would be nice to have but wouldn't drastically affect the chart were omitted.
2.For covering each line, the number of pages in this tutorial would be huge. Instead, future tutorials will focus in detail on bits and pieces of this code.
With that said, I will provide an overview of what the code does and then cover some of the more interesting aspects in greater detail.
Code Overview
The amount of code written may seem like a lot, but hopefully after this section you will see that it is just many lines doing simple things. Many things will make sense when you understand that all of your chart data is stored as an array of integers.
For example, this is how your data might look like in the array:
int[] points = {10, 20, 15, 30, 5, 23, 19};
So you have seven data points with the range of numbers going from a minimum of 5 and a maximum of 23. Your goal is to plot the seven numbers while normalizing the chart for minimum and maximum values of 5 and 23.
Our program takes this integer array, maximum value, and minimum value and gets it to the chart form in the DrawChart method. Inside the DrawChart method, you specify the details of the chart itself. For example, properties such as how wide/tall the chart will be, where the chart offsets (gaps) are, etc, are specified.
Once you have the constraints of our chart specified, it is time to draw our chart. Drawing a line is essentially having a starting point and an ending point, and having infinitely small dots connecting both the starting and ending points. More realistically, a chart works by drawing a line from the first value to the second value, from the second value to the third value, etc. until your last value is reached.
Beyond this, you have a lot of code that generates the various lines, text labels, etc. We aren't using any GUI-based tools to draw the interface. Everything is done in code, and that can be a bit confusing if you have never designed an interface using only code.

Setting the Image Properties

The output of our code will be an image, and there are four lines that describe the image setup:
bmp = new Bitmap(400, 300);
gfx = Graphics.FromImage(bmp);
gfx.Clear(Color.White);
gfx.SmoothingMode = SmoothingMode.AntiAlias;
An image in computer-terms can be considered a bitmap where each pixel contains some color value. In the first line, I set the bitmap's dimensions to be 400 pixels wide and 300 pixels tall. I apply the bitmap by using the Graphics method to create an image out of our initial bmp definition.
bmp = new Bitmap(400, 300);
gfx = Graphics.FromImage(bmp);
gfx.Clear(Color.White);
gfx.SmoothingMode = SmoothingMode.AntiAlias;
Now that we assigned our bitmap to our Graphics gfx variable, we won't directly be dealing with our Bitmap object for a while. With the gfx.Clear line, I am essentially clearing the background and setting a default White color.
When you draw shapes and lines, by default they are quite jagged. In order to have them look smoother - antialiased - you will need to set your graphic object's SmoothingMode property to SmoothingMode.AntiAlias.
The following is an image of our chart without the SmoothingMode set to AntiAlias:

Notice the the corners and edges of the lines have a very jagged feel to them.
Getting the Maximum and Minimum Values
The data points plotted are originally stored in an array. In the code, you will see that a generateRandomValues() method returns an array containing what the method name promises, random values.
There are many ways to determine the maximum values. One, less efficient way, is the method I explained for Flash in the following tutorial. In this code, I take a more efficient approach:
int[] p = generateRandomValues();

int[] k = new int[p.Length];

Array.Copy(p, k, p.Length);
Array.Sort(k);

DrawChart(p, k[k.Length - 1], k[0]);
In this code, what I am doing is storing the array of numbers in the p variable. I then make a copy of the p variable and store it into array k. I will explain why I do that in a few lines.
The Array.sort() method takes an array as its argument and sorts numbers inside it from largest to smallest. Therefore, the array's numbers would be ordered in smallest to largest like:
k = [smallest,...,largest];
So by simply taking the first value, I get the smallest value in the array. By taking the last value, I get the largest value in the array. Because the Array.sort() method modifies the contents of the array itself, I cannot plot array k. If I were to plot array k, my chart would plot the sorted array instead of the original, random array.
To display my original array, I create a copy of my array prior to actually sorting the array's numbers. That way, I independently have both the max and min values while still having a copy of my original array with which to plot the various numbers.

Normalizing the Chart

One of my design goals was to make the chart be capable of plotting a wide range of values. Like I mentioned earlier, you are constrained by your chart's height and width, so you will need to ensure that the highest and lowest values of your chart display in a reasonably accurate scale.
The following is the code that normalizes the chart's height and width:
// Taking care of some bookwork (declaring/initializing variables)
int maxDataPoints = points.Length;
int chartHeight = bmp.Height - bottomOffset;
int chartWidth = bmp.Width - rightOffset;

// Adjustable Values
double adjustedMax = maxValue * .10 + maxValue;
double adjustedMin = minValue - .50 * minValue;
double adjustVerticalRatio = (chartHeight-topOffset) / adjustedMax;
double adjustHorizontalRatio = ((chartWidth - leftOffset) / (maxDataPoints - 1));
One good way of looking at this is via an example. Let's say your maximum data point is 1000, and your chart's height is 100 pixels. That means, for every pixel, you have to cover 10 points of data so that you can display your 1000-value data point. That also means that a value of 500, will be 50 pixels high. Similarly, a data point that is 100, will only be 10 pixels tall.
What you are trying to do is come up with a good ratio between your chart's height and the number of data values each pixel will cover. The ratio is, as shown above, determined only by your maximum data point, and the ratio is what the adjustVerticalRatio variable stores.
Likewise for the horizontal case, the adjustHorizontalRatio variable stores the distance each data point must be separated by in order to fill up the horizontal space. Ideally, that would be the chart's width divided by the number of data points.
Notice in a lot of the above cases, I take into account any vertical or horizontal offsets that you may have introduced.
Plotting the Lines
To plot the lines, you first determine the starting and ending X and Y positions. Because in order to draw any lines, you need a starting point as well as an ending point. In our case, since all the lines are interconnected, your ending point for one line is the starting point for the next line.
The code for determining the two x and y positions can be found below:
int xPos = Convert.ToInt32(i * adjustHorizontalRatio) + leftOffset;
int xPos2 = Convert.ToInt32((i + 1) * adjustHorizontalRatio) + leftOffset;

int yPos = Convert.ToInt32(chartHeight - adjustVerticalRatio * points[i]);
int yPos2 = Convert.ToInt32(chartHeight - adjustVerticalRatio * points[i + 1]);
Some key things to note are how the earlier adjustHorizontalRatio and adjustVerticalRatio values are being used. Notice also that I am converting all of the earlier data into integers, for as you will see in the next line, the x and y positions are specified as a Point object that only accepts integer values:
gfx.DrawLine(chartPen, new Point(xPos, yPos), new Point(xPos2, yPos2));
Drawing Border Lines
If you look at the chart, you will see that it contains border lines that clearly separate the chart from the rest of the drawing area. Notice that the code for drawing the border lines is not contained inside the for loop. It only needs to be drawn once, it is outside of the loop.
//Draw Border Lines
Pen borderLine = new Pen(Color.DarkGray, 2);

//Left Border
gfx.DrawLine(borderLine, new Point(leftOffset, chartHeight), new Point(leftOffset, topOffset));

//Bottom Border
gfx.DrawLine(borderLine, new Point(leftOffset, chartHeight), new Point(chartWidth, chartHeight));

//Right Border
gfx.DrawLine(borderLine, new Point(chartWidth, chartHeight), new Point(chartWidth, topOffset));

//Top Border
gfx.DrawLine(borderLine, new Point(leftOffset, topOffset), new Point(chartWidth, topOffset));
The DrawLine method takes three arguments in this case. The first argument is a Pen object that defines both the color and thickness of the line. The second and third arguments are our familiar Point values that each take an x and y integer value.
Notice that the arguments passed into Point correspond to the edges as defined earlier in the following image:

Outputting as an Image
I started off the code explanation by describing how you would setup your image's bitmap and graphics variables. We go full circle now and have reached the point where we close the image and set its output properties.
The code for outputting the final result as an image is as follows:
//Finalizing and Cleaning Up
Response.ContentType = "image/jpeg";
bmp.Save(Response.OutputStream, ImageFormat.Jpeg);
bmp.Dispose();
gfx.Dispose();
Response.End();
Notice I set the content type as "image/jpeg" to ensure the output is set to the JPEG format. An image is created on the fly each time your aspx page is accessed, so if you were to place your aspx page between img tags in HTML, you will see the image produced by your code.
Conclusion

I hope this article gave you a brief understanding of not only how to approach designing a chart, but also how to draw in ASP.net.
I hope the information helped. If you have any questions or comments, please don't hesitate to post them on the kirupa.com Forums. Just post your question and I, or our friendly forum helpers, will help answer it.