The answer below the line was written in 2008.
C# 7 introduced pattern matching, which has largely replaced the as
operator, as you can now write:
if (randomObject is TargetType tt)
{
// Use tt here
}
Note that tt
is still in scope after this, but not definitely assigned. (It is definitely assigned within the if
body.) That's slightly annoying in some cases, so if you really care about introducing the smallest number of variables possible in every scope, you might still want to use is
followed by a cast.
I don't think any of the answers so far (at the time of starting this answer!) have really explained where it's worth using which.
Don't do this:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
Not only is this checking twice, but it may be checking different things, if randomObject
is a field rather than a local variable. It's possible for the "if" to pass but then the cast to fail, if another thread changes the value of randomObject
between the two.
If randomObject
really should be an instance of TargetType
, i.e. if it's not, that means there's a bug, then casting is the right solution. That throws an exception immediately, which means that no more work is done under incorrect assumptions, and the exception correctly shows the type of bug.
// This will throw an exception if randomObject is non-null and
// refers to an object of an incompatible type. The cast is
// the best code if that's the behaviour you want.
TargetType convertedRandomObject = (TargetType) randomObject;
If randomObject
might be an instance of TargetType
and TargetType
is a reference type, then use code like this:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
If randomObject
might be an instance of TargetType
and TargetType
is a value type, then we can't use as
with TargetType
itself, but we can use a nullable type:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Note: currently this is actually slower than is + cast. I think it's more elegant and consistent, but there we go.)
If you really don't need the converted value, but you just need to know whether it is an instance of TargetType, then the is
operator is your friend. In this case it doesn't matter whether TargetType is a reference type or a value type.
There may be other cases involving generics where is
is useful (because you may not know whether T is a reference type or not, so you can't use as) but they're relatively obscure.
I've almost certainly used is
for the value type case before now, not having thought of using a nullable type and as
together :)
EDIT: Note that none of the above talks about performance, other than the value type case, where I've noted that unboxing to a nullable value type is actually slower - but consistent.
As per naasking's answer, is-and-cast or is-and-as are both as fast as as-and-null-check with modern JITs, as shown by the code below:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 30000000;
static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i + 1] = "x";
values[i + 2] = new object();
}
FindLengthWithIsAndCast(values);
FindLengthWithIsAndAs(values);
FindLengthWithAsAndNullCheck(values);
}
static void FindLengthWithIsAndCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = (string) o;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and Cast: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithIsAndAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = o as string;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and As: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithAsAndNullCheck(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
string a = o as string;
if (a != null)
{
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("As and null check: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
}
On my laptop, these all execute in about 60ms. Two things to note:
- There's no significant difference between them. (In fact, there are situations in which the as-plus-null-check definitely is slower. The above code actually makes the type check easy because it's for a sealed class; if you're checking for an interface, the balance tips slightly in favour of as-plus-null-check.)
- They're all insanely fast. This simply will not be the bottleneck in your code unless you really aren't going to do anything with the values afterwards.
So let's not worry about the performance. Let's worry about correctness and consistency.
I maintain that is-and-cast (or is-and-as) are both unsafe when dealing with variables, as the type of the value it refers to may change due to another thread between the test and the cast. That would be a pretty rare situation - but I'd rather have a convention which I can use consistently.
I also maintain that the as-then-null-check gives a better separation of concerns. We have one statement which attempts a conversion, and then one statement which uses the result. The is-and-cast or is-and-as performs a test and then another attempt to convert the value.
To put it another way, would anyone ever write:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
That's sort of what is-and-cast is doing - although obviously in a rather cheaper way.
A POCO follows the rules of OOP. It should (but doesn't have to) have state and behavior. POCO comes from POJO, coined by Martin Fowler [anecdote here]. He used the term POJO as a way to make it more sexy to reject the framework heavy EJB implementations. POCO should be used in the same context in .Net. Don't let frameworks dictate your object's design.
A DTO's only purpose is to transfer state, and should have no behavior. See Martin Fowler's explanation of a DTO for an example of the use of this pattern.
Here's the difference: POCO describes an approach to programming (good old fashioned object oriented programming), where DTO is a pattern that is used to "transfer data" using objects.
While you can treat POCOs like DTOs, you run the risk of creating an anemic domain model if you do so. Additionally, there's a mismatch in structure, since DTOs should be designed to transfer data, not to represent the true structure of the business domain. The result of this is that DTOs tend to be more flat than your actual domain.
In a domain of any reasonable complexity, you're almost always better off creating separate domain POCOs and translating them to DTOs. DDD (domain driven design) defines the anti-corruption layer (another link here, but best thing to do is buy the book), which is a good structure that makes the segregation clear.
Best Answer
This is made rather more complex since it depends which version of sql server you are willing to target.
Here is a guide to how sql server will deal with datatypes in replication scenarios
SQL Server Compact Edition adds further confusion as it doesn't support the varchar(max), varbinary(max) column types (you are limited to explicit ~4K length columns).
At some point you will also need to make a heuristic decision on areas with multiple options.
Given all this going with a nice simple switch statement in a utility function will make life much easier than attempting to rely on some opaque BCL library intended only for type translation rather than textual sql creation.
It also makes it clear via a default throw or default varchar(max) what your 'not yet defined' behaviour will be which will remain under your control.
Returning a simple immutable class from the method of the form:
You may want to add optional precision/size value too though that might be something you choose to leave to the user.