1

In my code I have registered route in global.asax like the following:

void RegisterRoutes(RouteCollection routes)
{
   routes.MapPageRoute(
    "product",
    "product/{id}",
    "~/index.aspx"
);

then in my index.aspx page, I want to test the {id} value whether it is integer:

if (Page.RouteData.Values["id"] as int? != null)   
//always evaluate as null even id is an integer in the URL, ex: /product/1

I understand I can use some other methods (such as int.TryParse or this ) to achieve the same goal, but would using a "as" make sense in this case?

Community
  • 1
  • 1
HOKBONG
  • 795
  • 7
  • 17
  • 4
    You have [`is`](http://msdn.microsoft.com/en-us/library/scekt9xw(v=vs.100).aspx) operator as well. (pun intended :)) – Shadow The GPT Wizard Oct 03 '12 at 14:52
  • If you simply pass it in, the model binder will do its best - if it is the only overload of your action and the value cannot be converted, you will get an exception. – Oded Oct 03 '12 at 14:55
  • @ShadowWizard, `is` would probably not work if the `id` value was stored as a string (which I'm going to guess it is). In that case it's probably better to `TryParse`. – Kiril Oct 03 '12 at 14:56
  • @Link but so will the `as` operator. Anyway, if the goal is to check the **value** (rather than type) the only way is TryParse indeed. – Shadow The GPT Wizard Oct 03 '12 at 14:58
  • @user: `is` still works even id is string – cuongle Oct 03 '12 at 14:58
  • I reckon `Page.RouteData.Values["id"]` is not an `integer`. – Jodrell Oct 03 '12 at 14:59
  • Thanks guy. I think as and is would behave the same in this case as they both test the type not the value. So I agree that probably TryParse is the way to go. – HOKBONG Oct 03 '12 at 15:00
  • @CuongLe It works as in it won't crash, it will just always return false. The goal of the OP appears to be determining if a string value can be parsed into a valid number; `is` won't tell you that. `"0" is int` will be `false`, not `true`. – Servy Oct 03 '12 at 15:13
  • @CuongLe In my mind "not crashing" isn't the same as "it works". "it works" means, performs the desired action. – Servy Oct 03 '12 at 15:16

5 Answers5

5

The way to go depends on what is really stored in Values["id"]. If it is in fact an int, you can test for int with Values["id"] is int. The Values[] property is typed as object. Therefore it can be null. But this does NOT mean, that the stored int is a Nullable<int>. (see my update below.)

If you assign an int (and all other non-reference types) to an object variable or property, it will be boxed. I.e. it will be packed into an object of the corresponding type. int or System.Int32 exists as value type as well as a boxed and immutable System.Int32 reference type. Because of its immutability, you don't notice that it is a reference type. An int can be boxed with object obj = 5; and unboxed with int i = (int)obj;.

On the other hand, if the id was stored as string then you would have to convert it to a number

int id;
if (Int32.TryParse((string)Page.RouteData.Values["id"], out id)) {
    // use id here
}

UPDATE:

There seems to be some confusion with null and Nullable<T>.

int? i; // Same as Nullable<int> or Nullable<System.Int32> or System.Int32?
object obj;
bool test;

i = null;
test = i.HasValue; // ==> false
test = i == null;  // ==> true

// Now comes the strange part

obj = 5;           // The int is boxed here.
// Debugger shows type of obj as "object {int}"
test = obj is int;   // ==> true
test = obj is int?;   // ==> true
int x = (int)obj;  // ==> 5, the int is unboxed here.

// But

obj = null;
// Debugger shows type of obj as "object"
test = obj is int; // ==> false
test = obj is int?; // ==> false
test = obj == null; // ==> true

// And
i = 5;
obj = i; // i is a Nullable<int>
// Debugger shows type of obj as "object {int}"
test = obj is int; // ==> true
test = obj is int?; // ==> true
test = obj == null; // ==> false

i = null;
obj = i;
// Debugger shows type of obj as "object"
test = obj is int; // ==> false
test = obj is int?; // ==> false
test = obj == null; // ==> true

Both obj is int and obj is int? return true if obj contains an int number but both return false if obj contains null! A null Nullable<int> is converted to (object)null when asssigned to obj and is no more Nullable<int>.

The debugger shows the type of obj as object {int} when there is a number stored, no matter whether an int or an int? was assigned! The type of obj is object when null is stored.

So Chuong Le is in fact right!

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • Thanks. good explanation and I like that your code is straightforward. – HOKBONG Oct 03 '12 at 15:15
  • *The Values[] property is typed as object. Therefore it can be null* is not correct, try: `object a = 1; bool flag b = a is int?`, flag still true – cuongle Oct 03 '12 at 15:18
  • @CuongLe: You are confusing two things. You can assign `object a = null;`. This is NOT the same as `int? i = null;`! An `int?` with no value in it can be compared to `null` and yields `true`, but `null` is not a `Nullable`! The Comparison works because the `==` operator is overloaded in `Nullable`. – Olivier Jacot-Descombes Oct 03 '12 at 15:21
  • @CuongLe: Im am sorry, you are in fact right. I updated my post and added the results of tests I have made. The confusion comes from that fact that there is no difference between a boxed `int` and a boxed `int?` and both `obj is int` and `obj is int?` return `true`! – Olivier Jacot-Descombes Oct 03 '12 at 16:14
3

I would recommend that you use Int.TryParse, because the value is most likely going to be a string. Don't use the Int.Parse because you will also have to handle the exception and you generally want to avoid coding things that could throw exceptions (even if you're handling them).

Using as is not really a good solution since it forces you to use nullable int; if you take the is route, then it's almost guaranteed not to work when the passed in value is a string type.

Regarding the is debate, I ran this in VS:

string value = "42";
object answerToUniverse = value;
if (answerToUniverse is int)
{
    Console.WriteLine("It's an integer!");
}
else
{
    Console.WriteLine("It's NOT an integer!");
}

The result was: It's NOT an integer!

Kiril
  • 39,672
  • 31
  • 167
  • 226
0

Perhaps you actually want to do this,

var idString = Page.RouteData.Values["id"] as string;
int id;
if (int.TryParse(idString, out id))
{
   // id is now a valid number 
}
else
{
   // your types are confused
}
Jodrell
  • 34,946
  • 5
  • 87
  • 124
0

Looks like you want to check the value rather than the type meaning you don't really care what the type is.

In such case, this is the best approach I can recommend:

object idValue = Page.RouteData.Values["id"];
int dummy;
if (idValue != null && Int32.TryParse(idValue.ToString(), out dummy))
{
    //value is integer!
}

This way it doesn't matter what the type is; if it's integer, double, string or even complex type with proper ToString() override, it will work.

Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
  • `This way it doesn't matter what the type is` unless it's `null`, then you'll get a null reference exception. Additionally, if it really is an integer (I'm pretty sure it's not though) you'd be turning it into a string and then parsing it again. That's generally not a good idea. If it's possible that the value really is an integer you should check for it and then cast. – Servy Oct 03 '12 at 15:17
  • @Servy I do check for null first: `if (idValue != null && ...)` so no risk for error. This way is good only if the type is unknown, if it's known then of course casting is ideal. – Shadow The GPT Wizard Oct 03 '12 at 15:22
  • This way is okay if you know that the value is actually a string. If it's not you should check if it's an `int`, and cast it if it is, do the same for the other numeric types, and then if none of those succeed then call `ToString` and try to parse it. That is, in essense, what `Convert.ToInt` does, although it will throw an exception if none of the possible options work rather than failing gracefully. In practice though, you should never be in the situation where you sometimes have an integer and sometimes have a string with an integer value. – Servy Oct 03 '12 at 15:25
0

as is a safe cast. It will try and cast the provided value to the desired type, returning null instead of throwing an exception when a standard cast would.

Therefore unless Page.RouteData.Values["id"] was an integer object this would not work and you need to user the alternative methods you suggested.

One was could be to use an extension method

public static class IntExtensions {
   public static int? AsInt(this string source) {
     int result;
     return (int.TryParse(source,out result)) ? (int?)null : result;
   }

   public static bool IsInt(this string source) {
     return source.AsInt.HasValue;
   }
}

Allowing you to do

if (Page.RouteData.Values["id"].IsInt())
Bob Vale
  • 18,094
  • 1
  • 42
  • 49