Currently showing entries from August 2007
|
|
.NET Interview Questions - Part 2
August 26, 2007 • 9:41PM • permalink
Part 1 can be found here.
Continuing where we left off...
3) What does the term "immutable string" mean?
Strings in C# are immutable, meaning that the string object cannot be modified once it has been instantiated. Take a look at the following:
string s1 = "Hello, my name is ";
s1 += "adam";
While it appears that the string 'adam' is simply being appended to the original string, a lot more work is taking place on the back end. Since the string s1 is immutable, it can't be changed. Implicitly, c# is creating a new buffer in memory that can hold both the "My name is " and "adam". Once this new buffer is filled (and a new string object is created) s1 is assigned the new reference and the old references eventually become garbage collected.
If you read my blog entry on the System.Diagnostics.Stopwatch class, you saw that this can have a huge effect on the performance of an application.
In the Stopwatch blog entry, I used the string.Concat method in the optimized test. While that method outperforms the standard += operator (and can be further optimized by using the overloaded version that allows passing in four string parameters), there is an even better way, for doing many append operations.
using System.Text;
StringBuilder sb = new StringBuilder();
string letters = "abcdefghijklmnopqrstuvwxyz";
int iterations = 100000;
for (int i = 0; i < iterations; ++i)
sb.Append(letters[i % 26]);
In my tests, the StringBuilder class' Append method was twice as fast as using string.Concat. This is because the StringBuilder class has preallocated a buffer of memory upon initialization, which the string.Concat object is still creating extra immutable string objects that aren't needed.
4) Describe the concept of lazy-initialization in OOP?
Lazy-initialization is best shown with a working example...
Let us take for instance that we have a class that represents a Building, perhaps for a game. In this Building class, we have various properties to represent the different rooms that you might find in the building.
For sake of this argument, let us also suppose that the general architecture of the game dictates that the Building class reference is going to be persisted in a database and not in static memory (maybe it's web-based). When we create the instance of the Building object, it would be a huge mistake to instantiate all the various rooms of the Building in the constructor.
There could be a Kitchen, a JanitorCloset, a SupplyRoom, a Gym, a Hallway or maybe even Bathroom[]. If only one of the above is used in the current execution, then we would waste a lot of processing on both the web server and possibly on either a cache server or a database server (or both), retrieving data for the rooms we don't need. This is a very common mistake that is made by developers that are new to OOP.
Lazy-initialization can help us improve our performance by only creating the objects as they are needed. Like in the following example:
public class Building
{
private Room kitchen;
private Room supply_room;
...
public Room Kitchen
{
get {
if (kitchen == null)
kitchen = new Room(building_id, 'kitchen');
return kitchen;
}
}
public Room SupplyRoom
{
get {
if (supply_room == null)
supply_room= new Room(building_id, 'supply_room');
return supply_room;
}
}
}
In the code above, the first time the Kitchen or SupplyRoom properties are used, their associated private fields will be null and will be instantiated through their respective Room constructors. If they aren't used in a given request, they won't be instantiated at all!
5) Write a Generic Method that takes one parameter of any type and returns the same type that is passed in.
This question really isn't fair, but we like to throw it out there anyways. We don't base any candidate on their ability to answer it (because although 1 person actually got close, nobody has ever answered such a simple question). This is actually one of the first questions we ask, mostly to set the bar. Most of the time we see a person writing a function that attempts to use Generics, which is better than some other things I've seen... (Note that this is a .NET 2.0 specific feature)
The solution is actually very simple:
using System.Collections.Generic;
public T SomeFunc<T>(T obj)
{
T new_object_copy = obj;
return new_object_copy;
}
That's it! I only added the line T new_object_copy = obj to do something more interesting than just return the object.
We can now call this with virtually any type we want!
string s = SomeFunc<string>("hi");
int i = SomeFunc<int>(5);
my_class m = SomeFunc<my_class>(new my_class());
As a bonus, you can constrain the types of objects that you want to allow the function to operate on by adding a where clause:
public T SomeFunc<T>(T obj) where T : IList, IEnumerable
{
T new_object_copy = obj;
return new_object_copy;
}
List l = new List<string>();
List l2 = SomeFunc<List<string>>(l);
string s = SomeFunc<string>("hi");
Simple!
Maybe so, but we've yet to see somebody answer it correctly (and I've done literally fifty interviews in the last six months). We even joke around the office that if somebody can answer it correctly, they are an "instant hire". (Offer now null and void, since I've given the solution away.)

.NET Quickies
* What is the C# coalesce operator and how is it used?
The coalesce operator in C# is the ?? operator. It is used to do a conditional assignment to a variable, evaluating from left to right and stopping on the first non-null result. For example:
string a = null;
string b = "Yay!";
string c = a ?? b;
* What is the size (in bytes) of the following data types on a 32-bit machine: byte, short, int, float and double?
byte = 1 byte
short = 2 bytes
int = 4 bytes
float = 4 bytes
double = 8 bytes
* What is the result of bit-shifting an integer (either to the right or the left)?
Bit-shifting an integer to the left will multiply the number by two for each bit shifted.
Bit-shifting an integer to the right will divide the number by two for each bit shifted.
I could talk about simple interview questions forever! I'm sure that I will have much more to write about on this topic in the future!
Continuing where we left off...
3) What does the term "immutable string" mean?
Strings in C# are immutable, meaning that the string object cannot be modified once it has been instantiated. Take a look at the following:
string s1 = "Hello, my name is ";
s1 += "adam";
While it appears that the string 'adam' is simply being appended to the original string, a lot more work is taking place on the back end. Since the string s1 is immutable, it can't be changed. Implicitly, c# is creating a new buffer in memory that can hold both the "My name is " and "adam". Once this new buffer is filled (and a new string object is created) s1 is assigned the new reference and the old references eventually become garbage collected.
If you read my blog entry on the System.Diagnostics.Stopwatch class, you saw that this can have a huge effect on the performance of an application.
In the Stopwatch blog entry, I used the string.Concat method in the optimized test. While that method outperforms the standard += operator (and can be further optimized by using the overloaded version that allows passing in four string parameters), there is an even better way, for doing many append operations.
using System.Text;
//Place this at the top...
StringBuilder sb = new StringBuilder();
string letters = "abcdefghijklmnopqrstuvwxyz";
int iterations = 100000;
for (int i = 0; i < iterations; ++i)
sb.Append(letters[i % 26]);
In my tests, the StringBuilder class' Append method was twice as fast as using string.Concat. This is because the StringBuilder class has preallocated a buffer of memory upon initialization, which the string.Concat object is still creating extra immutable string objects that aren't needed.
4) Describe the concept of lazy-initialization in OOP?
Lazy-initialization is best shown with a working example...
Let us take for instance that we have a class that represents a Building, perhaps for a game. In this Building class, we have various properties to represent the different rooms that you might find in the building.
For sake of this argument, let us also suppose that the general architecture of the game dictates that the Building class reference is going to be persisted in a database and not in static memory (maybe it's web-based). When we create the instance of the Building object, it would be a huge mistake to instantiate all the various rooms of the Building in the constructor.
There could be a Kitchen, a JanitorCloset, a SupplyRoom, a Gym, a Hallway or maybe even Bathroom[]. If only one of the above is used in the current execution, then we would waste a lot of processing on both the web server and possibly on either a cache server or a database server (or both), retrieving data for the rooms we don't need. This is a very common mistake that is made by developers that are new to OOP.
Lazy-initialization can help us improve our performance by only creating the objects as they are needed. Like in the following example:
public class Building
{
private Room kitchen;
private Room supply_room;
...
public Room Kitchen
{
get {
if (kitchen == null)
kitchen = new Room(building_id, 'kitchen');
return kitchen;
}
}
public Room SupplyRoom
{
get {
if (supply_room == null)
supply_room= new Room(building_id, 'supply_room');
return supply_room;
}
}
}
In the code above, the first time the Kitchen or SupplyRoom properties are used, their associated private fields will be null and will be instantiated through their respective Room constructors. If they aren't used in a given request, they won't be instantiated at all!
5) Write a Generic Method that takes one parameter of any type and returns the same type that is passed in.
This question really isn't fair, but we like to throw it out there anyways. We don't base any candidate on their ability to answer it (because although 1 person actually got close, nobody has ever answered such a simple question). This is actually one of the first questions we ask, mostly to set the bar. Most of the time we see a person writing a function that attempts to use Generics, which is better than some other things I've seen... (Note that this is a .NET 2.0 specific feature)
The solution is actually very simple:
using System.Collections.Generic;
//Place this at the top...
public T SomeFunc<T>(T obj)
{
T new_object_copy = obj;
return new_object_copy;
}
That's it! I only added the line T new_object_copy = obj to do something more interesting than just return the object.
We can now call this with virtually any type we want!
string s = SomeFunc<string>("hi");
int i = SomeFunc<int>(5);
my_class m = SomeFunc<my_class>(new my_class());
As a bonus, you can constrain the types of objects that you want to allow the function to operate on by adding a where clause:
public T SomeFunc<T>(T obj) where T : IList, IEnumerable
{
T new_object_copy = obj;
return new_object_copy;
}
List
List
//This will work
string s = SomeFunc<string>("hi");
//This will no longer compile
Simple!
Maybe so, but we've yet to see somebody answer it correctly (and I've done literally fifty interviews in the last six months). We even joke around the office that if somebody can answer it correctly, they are an "instant hire". (Offer now null and void, since I've given the solution away.)
.NET Quickies
* What is the C# coalesce operator and how is it used?
The coalesce operator in C# is the ?? operator. It is used to do a conditional assignment to a variable, evaluating from left to right and stopping on the first non-null result. For example:
string a = null;
string b = "Yay!";
string c = a ?? b;
//The value in b will be assigned to c
* What is the size (in bytes) of the following data types on a 32-bit machine: byte, short, int, float and double?
byte = 1 byte
short = 2 bytes
int = 4 bytes
float = 4 bytes
double = 8 bytes
* What is the result of bit-shifting an integer (either to the right or the left)?
Bit-shifting an integer to the left will multiply the number by two for each bit shifted.
Bit-shifting an integer to the right will divide the number by two for each bit shifted.
I could talk about simple interview questions forever! I'm sure that I will have much more to write about on this topic in the future!
0 comments
HTTP Status Code 307 - Temporary Redirect
August 19, 2007 • 9:41AM • permalink
I'm sure that many of you haven't gotten past the title before saying, "No, a HTTP Status Code of 302 is the Temporary Redirect." I'd like to briefly explain the difference between the two and show you how you can benefit from a 307 redirect. Please also note, that this is a temporary redirect and should probably be avoided in most production situations. You will see one case in particular below where this can be useful.
In .NET, the standard way to redirect between pages is to use the Response.Redirect() method. This implicitly flushes the Response buffer and instead of sending back HTML (with a status code 200 OK), it sends the user a 302 Found code, meaning the requested page was found, but under a different URI. The server also sends back the new URI in the Location header that should be subsequently retrieved by the client.
This works in most cases, except for one minor problem: the case when you're trying to POST data to the server. There are many ways around this problem (including changing it to a GET/QueryString combo), but if a POST is necessary a 307 Temporary Redirect status code will indicate to the browser that the POST method should be retained.
This can be very valuable when developing on a machine with Windows XP Pro (and hence IIS 5.1 which doesn't allow you to identify web sites with host headers.) Under IIS, I setup multiple projects with the directory schema C:\Inetpub\wwwroot\ProjectName\. In my code, I prefix all links with an Web.config driven value that indicates the subdirectory off the root that the project resides in. This way, I can use the value /ProjectName/ in dev and / in production and the links will work in both environments. I don't like to pass these values around though, so when I recently wrote a small Flash application specifically for one website, I wanted to hardcode the SWF to POST to 'http://www.domainname.com/'. It seemed like a lot of work at first, but with a 307 redirect, it was simple!
If the Flash file was hardcoded to POST to 'http://www.domainname.com/PageName.aspx', you just create a file in the localhost root directory (C:\Inetpub\wwwroot\ in my example) called PageName.aspx and run the following on Page_Load():
Response.StatusCode = 307;
Response.RedirectLocation = "/ProjectName/PageName.aspx";
This will allow proper testing in IIS 5.1 and will redirect to the correct page with a POST method and all associated form data.
One final note as to why this should only be used for testing. Part of the definition of a 307 status code directs that browsers should prompt the user of the redirect and allow them to replicate the action (for older browsers). Firefox, in particular, is one browser that fully supports the standard and prompts the user, asking if they want to allow the form data to be sent via a POST. Since this could lead to a very poor user experience, I would recommend limiting use of the 307 status code to development and testing environments only.
In .NET, the standard way to redirect between pages is to use the Response.Redirect() method. This implicitly flushes the Response buffer and instead of sending back HTML (with a status code 200 OK), it sends the user a 302 Found code, meaning the requested page was found, but under a different URI. The server also sends back the new URI in the Location header that should be subsequently retrieved by the client.
This works in most cases, except for one minor problem: the case when you're trying to POST data to the server. There are many ways around this problem (including changing it to a GET/QueryString combo), but if a POST is necessary a 307 Temporary Redirect status code will indicate to the browser that the POST method should be retained.
This can be very valuable when developing on a machine with Windows XP Pro (and hence IIS 5.1 which doesn't allow you to identify web sites with host headers.) Under IIS, I setup multiple projects with the directory schema C:\Inetpub\wwwroot\ProjectName\. In my code, I prefix all links with an Web.config driven value that indicates the subdirectory off the root that the project resides in. This way, I can use the value /ProjectName/ in dev and / in production and the links will work in both environments. I don't like to pass these values around though, so when I recently wrote a small Flash application specifically for one website, I wanted to hardcode the SWF to POST to 'http://www.domainname.com/'. It seemed like a lot of work at first, but with a 307 redirect, it was simple!
If the Flash file was hardcoded to POST to 'http://www.domainname.com/PageName.aspx', you just create a file in the localhost root directory (C:\Inetpub\wwwroot\ in my example) called PageName.aspx and run the following on Page_Load():
Response.StatusCode = 307;
Response.RedirectLocation = "/ProjectName/PageName.aspx";
This will allow proper testing in IIS 5.1 and will redirect to the correct page with a POST method and all associated form data.
One final note as to why this should only be used for testing. Part of the definition of a 307 status code directs that browsers should prompt the user of the redirect and allow them to replicate the action (for older browsers). Firefox, in particular, is one browser that fully supports the standard and prompts the user, asking if they want to allow the form data to be sent via a POST. Since this could lead to a very poor user experience, I would recommend limiting use of the 307 status code to development and testing environments only.
Simple Speed Testing in .NET - System.Diagnostics.Stopwatch
August 18, 2007 • 6:48AM • permalink
One of the biggest problems with the .NET Framework is that so many new features are released with each major version that minor additions and features usually slip through the cracks. In my experience, very few classes have been overlooked during the transition from 1.1 to 2.0 as much as the Stopwatch class (System.Diagnostics.Stopwatch).
This is a replacement for what most people probably do to test two different blocks of code that perform the same action and compare the speed between them. The process usually goes something like: log the current time, run the test, take the difference between the current time and your logged time, rinse, repeat.
As an alternative, the Stopwatch class was built using low-level API calls, with less overhead than other .NET methods. If the hardware and Windows version of the computer support a high-resolution performance counter, it will use this counter instead of the standard PC clock.
Here is a simple example:
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
string s1 = "";
string s2 = "";
string letters = "abcdefghijklmnopqrstuvwxyz";
int iterations = 50000;
sw.Start();
for (int i = 0; i < iterations; ++i)
s1 += letters[i % 26];
sw.Stop();
Response.Write("Test 1: " + sw.ElapsedMilliseconds + "ms");
sw.Reset();
sw.Start();
for (int i = 0; i < iterations; ++i)
string.Concat(s2, letters[i % 26]);
sw.Stop();
Response.Write(" | Test 2: " + sw.ElapsedMilliseconds + "ms");
As it mentions above, don't forget to Reset your Stopwatch between tests or your subsequent tests will have their times inflated (since otherwise the Stopwatch will continue incrementing from the point that it was stopped).
The Stopwatch has a few other useful methods and properties, such as IsRunning and IsHighResolution. You can view the MSDN description of the Stopwatch class here.
In case you're curious, the string.Concat function outperformed the += operator by over 550 times in repeated tests on my machine. This is due to the fact that the += creates multiple (immutable) string objects, while the string.Concat uses a buffer technique to avoid that. More to come on this topic in a future post.
This is a replacement for what most people probably do to test two different blocks of code that perform the same action and compare the speed between them. The process usually goes something like: log the current time, run the test, take the difference between the current time and your logged time, rinse, repeat.
As an alternative, the Stopwatch class was built using low-level API calls, with less overhead than other .NET methods. If the hardware and Windows version of the computer support a high-resolution performance counter, it will use this counter instead of the standard PC clock.
Here is a simple example:
using System.Diagnostics;
//Note: make sure you have this at the top of the class
Stopwatch sw = new Stopwatch();
//do any preliminary processing here, to not inflate your test results
string s1 = "";
string s2 = "";
string letters = "abcdefghijklmnopqrstuvwxyz";
int iterations = 50000;
//TEST 1
sw.Start();
for (int i = 0; i < iterations; ++i)
s1 += letters[i % 26];
sw.Stop();
//END - TEST 1
Response.Write("Test 1: " + sw.ElapsedMilliseconds + "ms");
//note that you can also use sw.ElapsedTicks
sw.Reset();
//Don't forget to reset the Stopwatch before your second test!!!
//TEST 2
sw.Start();
for (int i = 0; i < iterations; ++i)
string.Concat(s2, letters[i % 26]);
sw.Stop();
//END - TEST 2
Response.Write(" | Test 2: " + sw.ElapsedMilliseconds + "ms");
As it mentions above, don't forget to Reset your Stopwatch between tests or your subsequent tests will have their times inflated (since otherwise the Stopwatch will continue incrementing from the point that it was stopped).
The Stopwatch has a few other useful methods and properties, such as IsRunning and IsHighResolution. You can view the MSDN description of the Stopwatch class here.
In case you're curious, the string.Concat function outperformed the += operator by over 550 times in repeated tests on my machine. This is due to the fact that the += creates multiple (immutable) string objects, while the string.Concat uses a buffer technique to avoid that. More to come on this topic in a future post.
10 Things This Skeptic Loves About Python's Syntax
August 15, 2007 • 9:38AM • permalink
First, a note...
Anyone that has worked with me in the past will know what a skeptic I am about new programming languages. I use a lot of different languages (new and old) and operating systems (mostly to fit the constraints of my client's environments) but I prefer to work in C++/C# and in Microsoft Visual Studio. I am always reading one tech book or another, so as a change of pace I started doing some random reading about Python.
While I still prefer C++/C#, the seamless integration of Python with C is very appealing. There is no question that Python is a RAD programming language, especially when looking over the syntax. I wanted to mention ten of my favorites, although there are many more than this - and I've just started learning it! I'm going to give very simple explanations to save space, but I suggest if you find the below interesting that you consider learning Python.
(Please note that the below are in no specific order.)
Divide-and-Floor Operator //
This is an additional divide operator that is used to truncate the entire decimal portion. This is a full truncation of the number with no rounding involved. Since this is a fairly common operation when dealing with floating-point numbers, it's nice to have a simple operator to handle it.
print 10.0 / 3
print 10.0 // 3
x = 10.0
x //= 3
print x
Multiple Assignments In One Statement
Why waste space and time when you need to get things done? Python allows you to perform multiple actions in one statement through use of a comma. Items on either end of the equals sign are indexed in order, as seen here:
x, y = 1, 2
print x + y
increment, decrement = x + y, x - y
print increment, decrement
Scope Delimiters
Python takes a very unique way of delimiting blocks of code: by using whitespace. Lines of code with the same indentations (after the start of a conditional block of code or other scoped construct) are considered part of the same block.
if x > 3:
print 'this is inside the if-statement'
print 'this is too!'
print 'this is executed regardless of whether x is greater than 3'
sprintf Operator %
StringBuilder.AppendFormat is probably one of the .NET functions that I use the most. At 26 letters, even with Intellisense, it's a handful.
Python has a much simpler way of doing the same thing:
print "I eat about %0.2f times every %d days" % (123.456789, 10)
Use of the else Keyword with Loops
Python allows you to append an else clause to both while and for loops that always execute when a break clause is not executed inside the conditional block.
In many situations this allows for the elimination of some branching logic, in addition to allowing developers to remove an additional variable, in many cases, that would only serve to act as a flag for whether or not the intended case executed.
A common example is to implement simple search functionality:
search_number = 699
for i in range(1, 1000):
if (i == search_number):
print 'your number was found'
break
else:
print 'your number was not found'
search_number = 10000
for i in range(1, 1000):
if (i == search_number):
print 'your number was found'
break
else:
print 'your number was not found'
Slice Operator [:]
Nevermind taking a portion of a string, this technique can also be used to take a subset of a list or a dictionary (Python's version of arrays/vectors and hashtables). The example below uses a list.
L = [1, 2, 3, 4, 5, 6]
print L[2:4]
print L[2:]
print L[:3]
print L[:]
Step Operator [::]
This is really an additional parameter to the Slice Operator, but this determines the direction and quantity of the index increment (or decrement), as if iterating through the list. It's best to show with an example or three:
L = [1, 2, 3, 4, 5, 6]
print L[::-1]
print L[::3]
print L[::-2]
Note that this can (obviously) be combined with the Slice Operator above to change the rate of index through sublists. As a continuation from the example above:
print L[:1:-1]
Optional Function Arguments (* and ** Operators)
The whole reason you can use a different programming language in a given situation is the fundamental rule that you should always use the right tool for the right job. In Python, these are known as Arbitrary Function Arguments.
Python gives developers several options (and several combinations of the options) that can be used when using function overloading with optional arguments.
The first is the * operator which returns the remaining arguments of a function in a Python data type known as a tuple.
def SomeFunction(first_arg, *other_args):
print len(other_args)
SomeFunction('test', 'test2', 'test3', 4)
The other operator is the **operator which performs the same action, only returning a dictionary instead of a tuple. This only works with named arguments and will automatically map the argument name to the requested value.
def SomeFunction(**the_args):
print the_args
SomeFunction(a=1, b=2, c=3, d=4, e=5, f=6)
Multiple Comparison Operators
Unlike C and most other languages, Python allows you to chain the various comparison operations together to form complex expressions, just like in algebra. Note that like other boolean expressions, these are short-circuited to increase performance.
a = 1
b = 3
c = 5
print a < b < c
print c < b < a
Stop Reinventing the Wheel
Finally, Python takes many higher level constructs and implementations that have been propagated into multiple languages over the years.
Three (out of more than I can easily count) are shown below: simple string duplication, (theoretically) infinitely sized numbers and the Complex number type. These are shown very briefly below:
print "*" * 20
print 2 ** 100
x = 1 + 3j
y = 1 - 3j
print x * y
If you've ever programmed in any other language, you can see that Python has an extremely unique syntax. It is very unique, but surprisingly intuitive once you've begun to use it. The above is only a small example of how Python syntax varies from other languages. Now, I finally understand what people mean when they refer to Python code as being very "un-Python-like". Since Python supports most of the basic constructs of other languages, it's very easy to do most actions one of two ways: the normal way and the Python way.
Anyone that has worked with me in the past will know what a skeptic I am about new programming languages. I use a lot of different languages (new and old) and operating systems (mostly to fit the constraints of my client's environments) but I prefer to work in C++/C# and in Microsoft Visual Studio. I am always reading one tech book or another, so as a change of pace I started doing some random reading about Python.
While I still prefer C++/C#, the seamless integration of Python with C is very appealing. There is no question that Python is a RAD programming language, especially when looking over the syntax. I wanted to mention ten of my favorites, although there are many more than this - and I've just started learning it! I'm going to give very simple explanations to save space, but I suggest if you find the below interesting that you consider learning Python.
(Please note that the below are in no specific order.)
Divide-and-Floor Operator //
This is an additional divide operator that is used to truncate the entire decimal portion. This is a full truncation of the number with no rounding involved. Since this is a fairly common operation when dealing with floating-point numbers, it's nice to have a simple operator to handle it.
print 10.0 / 3
#prints 3.3333333333333335
print 10.0 // 3
#prints 3.0
x = 10.0
x //= 3
print x
#prints 3.0
Multiple Assignments In One Statement
Why waste space and time when you need to get things done? Python allows you to perform multiple actions in one statement through use of a comma. Items on either end of the equals sign are indexed in order, as seen here:
x, y = 1, 2
print x + y
#prints 3
increment, decrement = x + y, x - y
print increment, decrement
#prints 3, -1
Scope Delimiters
Python takes a very unique way of delimiting blocks of code: by using whitespace. Lines of code with the same indentations (after the start of a conditional block of code or other scoped construct) are considered part of the same block.
if x > 3:
print 'this is inside the if-statement'
print 'this is too!'
print 'this is executed regardless of whether x is greater than 3'
sprintf Operator %
StringBuilder.AppendFormat is probably one of the .NET functions that I use the most. At 26 letters, even with Intellisense, it's a handful.
Python has a much simpler way of doing the same thing:
print "I eat about %0.2f times every %d days" % (123.456789, 10)
#prints I eat about 123.46 times every 10 days
Use of the else Keyword with Loops
Python allows you to append an else clause to both while and for loops that always execute when a break clause is not executed inside the conditional block.
In many situations this allows for the elimination of some branching logic, in addition to allowing developers to remove an additional variable, in many cases, that would only serve to act as a flag for whether or not the intended case executed.
A common example is to implement simple search functionality:
search_number = 699
for i in range(1, 1000):
if (i == search_number):
print 'your number was found'
break
else:
print 'your number was not found'
#prints your number was found
search_number = 10000
for i in range(1, 1000):
if (i == search_number):
print 'your number was found'
break
else:
print 'your number was not found'
#prints your number was not found
Slice Operator [:]
Nevermind taking a portion of a string, this technique can also be used to take a subset of a list or a dictionary (Python's version of arrays/vectors and hashtables). The example below uses a list.
L = [1, 2, 3, 4, 5, 6]
#a (0-based) list
print L[2:4]
#prints [3, 4]
print L[2:]
#prints [3, 4, 5, 6]
print L[:3]
#prints [1, 2, 3]
print L[:]
#prints [1, 2, 3, 4, 5, 6]
Step Operator [::]
This is really an additional parameter to the Slice Operator, but this determines the direction and quantity of the index increment (or decrement), as if iterating through the list. It's best to show with an example or three:
L = [1, 2, 3, 4, 5, 6]
#a (0-based) list
print L[::-1]
#prints [6, 5, 4, 3, 2, 1]
print L[::3]
#prints [1, 4]
print L[::-2]
#prints [6, 4, 2]
Note that this can (obviously) be combined with the Slice Operator above to change the rate of index through sublists. As a continuation from the example above:
print L[:1:-1]
#prints [6, 5, 4, 3]
Optional Function Arguments (* and ** Operators)
The whole reason you can use a different programming language in a given situation is the fundamental rule that you should always use the right tool for the right job. In Python, these are known as Arbitrary Function Arguments.
Python gives developers several options (and several combinations of the options) that can be used when using function overloading with optional arguments.
The first is the * operator which returns the remaining arguments of a function in a Python data type known as a tuple.
def SomeFunction(first_arg, *other_args):
print len(other_args)
SomeFunction('test', 'test2', 'test3', 4)
#prints 3
The other operator is the **operator which performs the same action, only returning a dictionary instead of a tuple. This only works with named arguments and will automatically map the argument name to the requested value.
def SomeFunction(**the_args):
print the_args
SomeFunction(a=1, b=2, c=3, d=4, e=5, f=6)
#prints {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
Multiple Comparison Operators
Unlike C and most other languages, Python allows you to chain the various comparison operations together to form complex expressions, just like in algebra. Note that like other boolean expressions, these are short-circuited to increase performance.
a = 1
b = 3
c = 5
print a < b < c
#prints True
print c < b < a
#prints False
Stop Reinventing the Wheel
Finally, Python takes many higher level constructs and implementations that have been propagated into multiple languages over the years.
Three (out of more than I can easily count) are shown below: simple string duplication, (theoretically) infinitely sized numbers and the Complex number type. These are shown very briefly below:
#1) Simple String Extension
print "*" * 20
#prints ********************
#2) Infinitely Sized Numbers (based on Memory Limits)
#Note that ** is the exponent operator in Python.
#Normally 2 ** 64 is the long type limit in most systems.
print 2 ** 100
#prints 1267650600228229401496703205376L
#3) Complex Number Type
x = 1 + 3j
y = 1 - 3j
print x * y
#prints (10+0j)
If you've ever programmed in any other language, you can see that Python has an extremely unique syntax. It is very unique, but surprisingly intuitive once you've begun to use it. The above is only a small example of how Python syntax varies from other languages. Now, I finally understand what people mean when they refer to Python code as being very "un-Python-like". Since Python supports most of the basic constructs of other languages, it's very easy to do most actions one of two ways: the normal way and the Python way.
Book Review: Microsoft Windows Internals
August 12, 2007 • 1:06AM • permalink
Microsoft Windows Internals by David Solomon and Mark Russinovich dictates the internals of multiple parts of the Windows operating system, mainly focusing on Windows XP, Windows 2000 and Windows Server 2003 (including 64-bit versions and any IA-64 specifics). The book is directed at Operating System and Device Driver developers and every concept coupled with specific Win32 API functions or kernel variables that can be used to follow along or test out the concepts it presents. Additionally, sysadmins will be able to use the book as a very low-level reference manual since the kernel variables are matched up to performance counters that can be viewed using the standard Windows Performance Monitor.
The book moves from the basic systems that power the operating system using a layering technique to build your knowledge of Windows as you read. Startup, shutdown, processes, threads, virtual memory, security, I/O, caching and more - and then ending with the higher-level support that Windows provides for networking and a simple crash analysis of a basic BSoD (Blue-Screen-of-Death). The book also touches on several protocols like TCP/IP, uPnP and NTFS which helps give developers an overall understanding of how the different systems work together to drive the Windows user experience.
Occasionally it reads like a bloated advertisement for Russinovich's SysInternals toolset (http://www.sysinternals.com) - that is until you take the time to download any of the tools. Process Explorer in particular is one of the most valuable Windows Tools I've ever encountered and eliminates any need for the primitive Task Manager that you might have had in the past. The tools are used throughout the text in experiments that illustrate various parts of the Windows kernel.
The book moves from the basic systems that power the operating system using a layering technique to build your knowledge of Windows as you read. Startup, shutdown, processes, threads, virtual memory, security, I/O, caching and more - and then ending with the higher-level support that Windows provides for networking and a simple crash analysis of a basic BSoD (Blue-Screen-of-Death). The book also touches on several protocols like TCP/IP, uPnP and NTFS which helps give developers an overall understanding of how the different systems work together to drive the Windows user experience.
Occasionally it reads like a bloated advertisement for Russinovich's SysInternals toolset (http://www.sysinternals.com) - that is until you take the time to download any of the tools. Process Explorer in particular is one of the most valuable Windows Tools I've ever encountered and eliminates any need for the primitive Task Manager that you might have had in the past. The tools are used throughout the text in experiments that illustrate various parts of the Windows kernel.

A screenshot of Process Explorer.
Additionally, the humor of Solomon and Russinovich keep an otherwise boring subject more interesting than it should be, including which Registry key is spoofed to display the current computer's processor in the Control Panel's System applet and a BSoD Screensaver to install on your co-worker's computers.
To sum it up, this is one of the best books about Windows Development that I've read since Petzold's classic "Programming Windows 95" (Programming Windows 4th edition). Anyone interested in learning more about the fundamentals of Windows development should pick up a copy. A better understanding of the systems underlying your programs will allow for better optimization and performance, as well as a means to perform some "down and dirty" debugging work at the kernel level.
How to Bring Up Task Manager During a Remote Desktop Session
August 08, 2007 • 9:38PM • permalink
This was something I'd wondered how to do for a long time. In Windows, all images (or exe files) that are started are created as children of the Winlogin.exe process, which is created when a user first logs in. As a [intentional] consequence of this, Winlogin.exe captures the Ctrl-Alt-Delete control key sequence which can't be overridden by another process.
Sometimes when connecting to another computer over the network, I might need to use Task Manager to suddenly terminate an application in order to free up either memory or reduce network latency from any applications that might have open connections. I'm used to using the Ctrl-Alt-Delete sequence, but since this is always hooked by the Winlogin.exe on the LOCAL computer, Remote Desktop can't transmit it over the connection.
Now that you know why - the solution! The control sequence Ctrl-Alt-End will act the same as a Ctrl-Alt-Delete when used in a Remote Desktop Session.
Sometimes when connecting to another computer over the network, I might need to use Task Manager to suddenly terminate an application in order to free up either memory or reduce network latency from any applications that might have open connections. I'm used to using the Ctrl-Alt-Delete sequence, but since this is always hooked by the Winlogin.exe on the LOCAL computer, Remote Desktop can't transmit it over the connection.
Now that you know why - the solution! The control sequence Ctrl-Alt-End will act the same as a Ctrl-Alt-Delete when used in a Remote Desktop Session.
Repeating a Batch in SQL Server 2005
August 08, 2007 • 4:56PM • permalink
I haven't found this feature documented anywhere, but I picked it up from one of Itzik Ben-Gan's articles.
Normally, you use the keyword GO when you need to force the previous batch of code to immediately execute, such as when using DDL or CTEs, like so:
CREATE TABLE [dbo].[my_table]
(
id INT
)
GO
While not useful in the case above, you can repeat a batch by placing an integer value immediately after the GO statement. This trick is very helpful when populating a table with random data or when doing repeated testing. So, for example, if we want to fill the above table with 1,000,000 rows of random integers, we can easily do it!
INSERT INTO [dbo].[my_table]
(id)
VALUES
(FLOOR(RAND() * 10000))
GO 1000000
Normally, you use the keyword GO when you need to force the previous batch of code to immediately execute, such as when using DDL or CTEs, like so:
CREATE TABLE [dbo].[my_table]
(
id INT
)
GO
While not useful in the case above, you can repeat a batch by placing an integer value immediately after the GO statement. This trick is very helpful when populating a table with random data or when doing repeated testing. So, for example, if we want to fill the above table with 1,000,000 rows of random integers, we can easily do it!
INSERT INTO [dbo].[my_table]
(id)
VALUES
(FLOOR(RAND() * 10000))
GO 1000000
.NET Interview Questions - Part 1
August 07, 2007 • 9:52AM • permalink
With MANY open .NET development positions in the Media Department, I've had many opportunities to refine my interview process. Below is a summary of my experience, including some of the general .NET questions we ask most job candidates at Demand Media (and why). Since we are constantly refining the interview process and adding new questions and since this is going to be a rather long entry, I'll split it up into several separate posts.
General Interview Notes
First, a couple of general caveats:
1) I try to give the interviewee the benefit of the doubt. A lot of the time, the people I talk to have more years of experience than I've been alive, so I understand that a lot of them are intimidated by my youth.
2) Look up something about the company on the Internet. Of course nobody has time to redo their entire resume to cater to a specific company, but look up something about the company and try to tune your responses to items that might relate.
3) Most interviews I've been on included the question: "Do you have any questions for me?" That's your cue. Ask any intelligent question without appearing to be a snob, but make sure you ask something (besides your compulsory question about which snacks are kept in which kitchen.) A few stand-by questions I used to use are: "Will I be able to continue my personal education and learn here?", "How quickly do you adapt to new technologies and upgrades to existing technologies?", "What is the team dynamic like?" and "How much room is there for career growth at XXXXXXX Company?"
4) Your resume is your first impression. Use it as a way to highlight your strengths, not as a laundry list of items you "used once in school". There are two main items in particular I see all the time: C++ and .NET 3.5 - usually only by candidates who can't explain ANY intricate features of either.
5) An interview should be a learning process, as well. If you don't understand a question or an answer to a question, ask!
6) TURN OFF YOUR CELL PHONE!!! For many people I've encountered during my career (including me), a cell phone going off during an interview is an instant death sentence. You can start juggling keyboards after that - it's not going to matter...
And now finally...
.NET Questions
1) Describe the difference between a reference type variable and a value type variable?
This is a good question we use to get rid of the riff-raff right away. If you can't form some semblence of an answer to this question, we won't even consider you a candidate for a job.
While we look for many keywords, a simple description of the fact that a value type stores the actual value of the data and the reference type stores an address or pointer to the data usually suffices.
If you don't somehow relate the reference and value types to their storage location on the stack and the heap, that will definitely become question 1b.
2) Describe the process of using an integer variable (of any size) as a bitmap for boolean flags?
This is a process I brought with me to Demand (inherited from Intermix Media, where I propagated the idea as well), but I think it's fairly clear why this is a simple, but vital idea. (Although, I'm assuming below a basic knowledge of binary numbers and bitwise mathematics.)
The idea is that any number can be viewed as a series of bytes and then bits. First I'll describe the process, then I'll demonstrate it in both basic C/C++/C# code and then using .NET additions.
As you should already know, any number can be represented in binary form, which means any number can be represented as a sum of one or more powers of two. We can show this number a number of ways:
123 (decimal) OR
(100 * 1) + (10 * 2) + (1 * 3) OR
(10^2 * 1) + (10^1 * 2) + (10^0 * 3) OR
(64 * 1) + (32 * 1) + (16 * 1) + (8 * 1) * (4 * 0) + (2 * 1) + (1 * 1) OR
(2^6 * 1) + (2^5 * 1) + (2^4 * 1) + (2^3 * 1) * (2^2 * 0) + (2^1 * 1) + (2^0 * 1) OR
1111011 (binary)
If the above binary number is extended to represent a 32-bit int,it would look like:
00000000000000000000000001111011
with the leftmost bit being bit 31 and the rightmost bit being bit 0.
This allows us to see the above as a series of 32 switches or Boolean (True/False) flags that can be accessed (counting from 0 and from the right) by taking a bitwise AND of the number and the "on" value.
As you can see from the above representation, the fourth bit from the right is in the 2^3 position. So, if we take the bitwise AND of our value with 2^3 (8), we get the following:
= 123 & 8
= 8
Since the result (8) is the same as the flag (8), the integer (123) does contain the flag!
When adding flags into your integer, you instead use the bitwise OR. This is always used to switch flags on, so if the flag already exists in your number - it will have no effect, as shown in the following examples:
= 123 | 8
= 123
= 123 | 4
= 127
Finally, to remove a class you take the bitwise AND of the bitwise NOT of the value you're trying to remove. The bitwise NOT inverts the value, so the AND masks any other bits that are currently set, like:
= 127 & (~4)
= 123
The example I usually like to use is to build out the character classes in an RPG-like setting.
Simple C-style Example:
/* CC_ is for Character Class */
int CC_DWARF = 1;
int CC_FIGHTER = 2;
int CC_NINJA = 4;
int CC_SAMURAI = 8;
int CC_ELF = 16;
int CC_MAGICIAN = 32;
int CC_PALADIN = 64;
int CC_HOBBIT = 128;
int CC_PRIEST = 256;
int CC_DARKLORD = 1073741824;
int character_dwarf_fighter = CC_DWARF | CC_FIGHTER;
int character_samurai_elf = CC_SAMURAI | CC_ELF;
int super_crazy_bad_guy = CC_FIGHTER | CC_NINJA | CC_SAMURAI | CC_MAGICIAN | CC_DARKLORD;
int super_crazy_bad_guy2 = super_crazy_bad_guy;
super_crazy_bad_guy2 &= ~(CC_DARKLORD);
if ((super_crazy_bad_guy2 & CC_DARKLORD) == CC_DARKLORD)
Response.Write("This guy is nothing now!");
if ((super_crazy_bad_guy2 & CC_PRIEST) == CC_PRIEST)
Response.Write("This guy is holier than I am!");
Simple .NET/C#-style Example:
[Flags]
public enum CharacterClasses : int
{
Dwarf = 1,
Fighter = 2,
Ninja = 4,
Samurai = 8,
Elf = 16,
Magician = 32,
Paladin = 64,
Hobbit = 128,
Priest = 256,
DarkLord = 1073741824
};
CharacterClasses super_crazy_bad_guy = CharacterClasses.Fighter | CharacterClasses.Ninja | CharacterClasses.Samurai | CharacterClasses.Magician | CharacterClasses.DarkLord;
Response.Write(super_crazy_bad_guy.ToString());
super_crazy_bad_guy -= CharacterClasses.DarkLord;
Response.Write(super_crazy_bad_guy.ToString());
super_crazy_bad_guy |= CharacterClasses.Elf;
Response.Write(super_crazy_bad_guy.ToString());
if ((super_crazy_bad_guy &= CharacterClasses.Elf) != 0)
Response.Write("An elf!");
if ((super_crazy_bad_guy &= CharacterClasses.Paladin) != 0)
Response.Write("A paladin!");
.NET Quickies
* What is the root class that all other .NET classes are derived from?
System.Object
* Name as many ways as you can think of to find a certain character in a string?
String.Contains, "Character Crawl" using any of (for/foreach, String.Chars, String.CharAt, String[], bit masking, plus many others), String.IndexOf, String.LastIndexOf, etc.
* What is a GUID?
Global Unique IDentifier - A 128-bit value that is statistically impossible to be duplicated in a closed environment. (Bonus points if you throw in a note about using it in a multi-server environment by setting the machinekey, generating on the SQL server, using a Key server, etc.)
More to come! Also, if anyone has any suggestions for questions that I should add into my interview process, please send me them!
General Interview Notes
First, a couple of general caveats:
1) I try to give the interviewee the benefit of the doubt. A lot of the time, the people I talk to have more years of experience than I've been alive, so I understand that a lot of them are intimidated by my youth.
2) Look up something about the company on the Internet. Of course nobody has time to redo their entire resume to cater to a specific company, but look up something about the company and try to tune your responses to items that might relate.
3) Most interviews I've been on included the question: "Do you have any questions for me?" That's your cue. Ask any intelligent question without appearing to be a snob, but make sure you ask something (besides your compulsory question about which snacks are kept in which kitchen.) A few stand-by questions I used to use are: "Will I be able to continue my personal education and learn here?", "How quickly do you adapt to new technologies and upgrades to existing technologies?", "What is the team dynamic like?" and "How much room is there for career growth at XXXXXXX Company?"
4) Your resume is your first impression. Use it as a way to highlight your strengths, not as a laundry list of items you "used once in school". There are two main items in particular I see all the time: C++ and .NET 3.5 - usually only by candidates who can't explain ANY intricate features of either.
5) An interview should be a learning process, as well. If you don't understand a question or an answer to a question, ask!
6) TURN OFF YOUR CELL PHONE!!! For many people I've encountered during my career (including me), a cell phone going off during an interview is an instant death sentence. You can start juggling keyboards after that - it's not going to matter...
And now finally...
.NET Questions
1) Describe the difference between a reference type variable and a value type variable?
This is a good question we use to get rid of the riff-raff right away. If you can't form some semblence of an answer to this question, we won't even consider you a candidate for a job.
While we look for many keywords, a simple description of the fact that a value type stores the actual value of the data and the reference type stores an address or pointer to the data usually suffices.
If you don't somehow relate the reference and value types to their storage location on the stack and the heap, that will definitely become question 1b.
2) Describe the process of using an integer variable (of any size) as a bitmap for boolean flags?
This is a process I brought with me to Demand (inherited from Intermix Media, where I propagated the idea as well), but I think it's fairly clear why this is a simple, but vital idea. (Although, I'm assuming below a basic knowledge of binary numbers and bitwise mathematics.)
The idea is that any number can be viewed as a series of bytes and then bits. First I'll describe the process, then I'll demonstrate it in both basic C/C++/C# code and then using .NET additions.
As you should already know, any number can be represented in binary form, which means any number can be represented as a sum of one or more powers of two. We can show this number a number of ways:
123 (decimal) OR
(100 * 1) + (10 * 2) + (1 * 3) OR
(10^2 * 1) + (10^1 * 2) + (10^0 * 3) OR
(64 * 1) + (32 * 1) + (16 * 1) + (8 * 1) * (4 * 0) + (2 * 1) + (1 * 1) OR
(2^6 * 1) + (2^5 * 1) + (2^4 * 1) + (2^3 * 1) * (2^2 * 0) + (2^1 * 1) + (2^0 * 1) OR
1111011 (binary)
If the above binary number is extended to represent a 32-bit int,it would look like:
00000000000000000000000001111011
with the leftmost bit being bit 31 and the rightmost bit being bit 0.
This allows us to see the above as a series of 32 switches or Boolean (True/False) flags that can be accessed (counting from 0 and from the right) by taking a bitwise AND of the number and the "on" value.
As you can see from the above representation, the fourth bit from the right is in the 2^3 position. So, if we take the bitwise AND of our value with 2^3 (8), we get the following:
= 123 & 8
= 8
Since the result (8) is the same as the flag (8), the integer (123) does contain the flag!
When adding flags into your integer, you instead use the bitwise OR. This is always used to switch flags on, so if the flag already exists in your number - it will have no effect, as shown in the following examples:
= 123 | 8
= 123
= 123 | 4
= 127
Finally, to remove a class you take the bitwise AND of the bitwise NOT of the value you're trying to remove. The bitwise NOT inverts the value, so the AND masks any other bits that are currently set, like:
= 127 & (~4)
= 123
The example I usually like to use is to build out the character classes in an RPG-like setting.
Simple C-style Example:
/* CC_ is for Character Class */
int CC_DWARF = 1;
int CC_FIGHTER = 2;
int CC_NINJA = 4;
int CC_SAMURAI = 8;
int CC_ELF = 16;
int CC_MAGICIAN = 32;
int CC_PALADIN = 64;
int CC_HOBBIT = 128;
int CC_PRIEST = 256;
int CC_DARKLORD = 1073741824;
int character_dwarf_fighter = CC_DWARF | CC_FIGHTER;
//value is 3
int character_samurai_elf = CC_SAMURAI | CC_ELF;
//value is 24
int super_crazy_bad_guy = CC_FIGHTER | CC_NINJA | CC_SAMURAI | CC_MAGICIAN | CC_DARKLORD;
//value is 1073741870
//let's remove the CC_DARKLORD class
int super_crazy_bad_guy2 = super_crazy_bad_guy;
super_crazy_bad_guy2 &= ~(CC_DARKLORD);
//value of super_crazy_bad_guy2 is now 46
//OR CC_FIGHTER | CC_NINJA | CC_SAMURAI | CC_MAGICIAN
//let's check super_crazy_bad_guy2 for CC_DARKLORD
if ((super_crazy_bad_guy2 & CC_DARKLORD) == CC_DARKLORD)
Response.Write("This guy is nothing now!");
//this will print...
//let's check super_crazy_bad_guy2 for CC_DARKLORD
if ((super_crazy_bad_guy2 & CC_PRIEST) == CC_PRIEST)
Response.Write("This guy is holier than I am!");
//this will NOT print...
Simple .NET/C#-style Example:
[Flags]
public enum CharacterClasses : int
{
Dwarf = 1,
Fighter = 2,
Ninja = 4,
Samurai = 8,
Elf = 16,
Magician = 32,
Paladin = 64,
Hobbit = 128,
Priest = 256,
DarkLord = 1073741824
};
CharacterClasses super_crazy_bad_guy = CharacterClasses.Fighter | CharacterClasses.Ninja | CharacterClasses.Samurai | CharacterClasses.Magician | CharacterClasses.DarkLord;
Response.Write(super_crazy_bad_guy.ToString());
//prints Fighter, Ninja, Samurai, Magician, DarkLord
super_crazy_bad_guy -= CharacterClasses.DarkLord;
Response.Write(super_crazy_bad_guy.ToString());
//prints Fighter, Ninja, Samurai, Magician
super_crazy_bad_guy |= CharacterClasses.Elf;
Response.Write(super_crazy_bad_guy.ToString());
//prints Fighter, Ninja, Samurai, Elf, Magician
if ((super_crazy_bad_guy &= CharacterClasses.Elf) != 0)
Response.Write("An elf!");
//this will print
if ((super_crazy_bad_guy &= CharacterClasses.Paladin) != 0)
Response.Write("A paladin!");
//this will NOT print
.NET Quickies
* What is the root class that all other .NET classes are derived from?
System.Object
* Name as many ways as you can think of to find a certain character in a string?
String.Contains, "Character Crawl" using any of (for/foreach, String.Chars, String.CharAt, String[], bit masking, plus many others), String.IndexOf, String.LastIndexOf, etc.
* What is a GUID?
Global Unique IDentifier - A 128-bit value that is statistically impossible to be duplicated in a closed environment. (Bonus points if you throw in a note about using it in a multi-server environment by setting the machinekey, generating on the SQL server, using a Key server, etc.)
More to come! Also, if anyone has any suggestions for questions that I should add into my interview process, please send me them!
Welcome to My Blog!
August 06, 2007 • 9:06AM • permalink
AdamWolkov.com has been live for almost two years, but there has never been much content on it. While I didn't have much time to spend on a redesign, I knew that I wanted to include a blog where I could write about my life, my family, my travels, my pets, my projects and anything else that comes to mind. I will also be using this blog as an aggregate by reprinting content that I post on other websites.
You can find more information about my various work and projects in the Me section. You can also find my personal photos on the photo site I keep with my wife, Adrianne (www.thewolkovs.com)
You can also contact me about anything at all. Clients (or companies requesting bids or more information) can use the same form.
So, welcome to my tiny spot on the web. I hope you enjoy!
You can find more information about my various work and projects in the Me section. You can also find my personal photos on the photo site I keep with my wife, Adrianne (www.thewolkovs.com)
You can also contact me about anything at all. Clients (or companies requesting bids or more information) can use the same form.
So, welcome to my tiny spot on the web. I hope you enjoy!
|
|
