views:

148

answers:

9

I have next code:

private T CreateInstance<T>(object obj) // where T : ISomeInterface, class
{
    ...

    if (!typeof(T).IsAssignableFrom(obj.GetType())) { throw ..; }

    return (T)obj;
}

Can it be replaced with this:

T result = obj as T;

if (result == null) { throw ..; }

return result;

If not - why?

+4  A: 

What about if (!(bar is T)) { throw ..; }

Alternatively if you don't need your own exception message the simplest answer is just to do:

return (T)obj;

The reason if that if it's not castable an InvalidCastException will be thrown and the return ignored. Unless you're adding some more logic or a custom error message there's no need to do a check and throw your own exception.

Davy8
A: 

Or even better because its easer to read true conditionals.

 if(obj is T){
    //Create instance. 
 }
 else{
    throw new InvalidArgumentException("Try Again");
 }
Nix
When you place "is" and then "as" is cause performance issues...http://stackoverflow.com/questions/686412/c-is-operator-performance/686431#686431
igor
Really? Performance is not in question here? He is not asking what is the fastest way to do it.
Nix
+1  A: 

See this post

The second one is safe...because at the first one if obj is null you will get exception (obj.GetType() --> NullReferenceException).

When you place "is" and then "as" is cause performance issues..

igor
I think he might need to add some Constraints but isn't that what he is trying to accomplish? If T can be this type... cast it?
Nix
+2  A: 

Maybe this (less brackets, better readability)

if (obj is T)
{
    return (T)obj;
}
else
   throw new ...

EDITED by reduced number of brackets I originally meant inverted check: ie

if (obj is T)

instead of

if (!(obj is T))

so final version can be

if (obj is T)
{
    return (T)obj;
}

throw new ...

or

if (obj is T)
{
    return (T)obj;
}
else
{
   throw new ...
}
desco
More error prone, when you omit brackets on else.
Nix
More error prone, because it makes the slightest bit of sense to insert another line of code after a `throw` statement?
Joel Mueller
+1  A: 

The class constraint where T : class allows you to use the as T statement.

private T CreateInstance<T>(object obj) where T : class
{
    if (!(obj is T)) { throw new ArgumentException("..."); }
    return obj as T;
}

or

private T CreateInstance<T>(object obj)
{
    if (!(obj is T)) { throw new ArgumentException("..."); }
    return (T)obj;
}
Daniel Dyson
Everything right except that it's better to use either `is` or `as` but not together. Double operation without sense.
abatishchev
True. I have updated my answer to reflect this. Thanks @abatishchev
Daniel Dyson
MSDN recommends next: `T result = obj as T; if (result != null ) { } else { }`. Like @Orsol's answer. Or yours - if `: class` constraint isn't available
abatishchev
Yes. All of these will work just fine. Looks like you might have answered your own question.
Daniel Dyson
+1  A: 

Another variant:

private T CreateInstance<T>(object obj) where T : ISomeInterface // as OP mentioned above
{
    ...

    T result = obj as T;
    if (result == null)
        { throw ..; }
    else 
       return result;
}
Orsol
Exactly what I mean!
abatishchev
Pity it won't compile. Did you try this?
Daniel Dyson
Why would you not want to check the type before you cast?
Nix
@Daniel: It just isn't compilable without a class constraint
abatishchev
@Daniel Dyson: of cause no. "throw ..;" doesn't allowed by C# compiler
Orsol
@Orsol, the original answer didn't have a class constraint (where T :...). I was able to add a new Exception("") to my code, but thanks for the idea. Of couse it still won't compile, because the constraint added in the edit is an interface constraint, not a class constraint. I would downvote twice if I could. :)
Daniel Dyson
@abatishchev: Didn't notice edits in question, so the answer is: "Yes it can be replaced"
Orsol
@Orsol: Very pity that your answer got so much negative votes. Undeservedly!
abatishchev
@abatishchev Спасибо, я привык к несовершенству этого мира :)
Orsol
@Orsol: Да пришел какой-то придурок и проминусовал весь пост!(
abatishchev
A: 

It may have been intended to handle cases where a conversion constructor would allow the operation, but apparently IsAssignableFrom doesn't handle that either. Don't see anything that can handle that. So I don't see how to check for cases like this:

class Program
{
  static void Main(string[] args)
  {
     B bValue = new B(123);
     Console.WriteLine(typeof(A).IsAssignableFrom(bValue.GetType()));
     //Console.WriteLine(bValue is A);
     //Console.WriteLine(bValue as A == null);
     A aValue = bValue;
     Console.WriteLine(aValue.ToString());
  }
}

class A
{
  string value;
  public A(string value)
  {
     this.value = value;
  }
  public override string ToString()
  {
     return value;
  }
}

class B
{
  int value;

  public B(int value)
  {
     this.value = value;
  }

  public static implicit operator A(B value)
  {
     return new A(value.value.ToString());
  }
}

In the end, I don't see any reason why you wouldn't want to use your version of the code, unless you want the code to throw an exception when obj is null. That's the only difference I can see. obj.GetType() will throw an null reference exception when obj is null instead of throwing the specified exception.

Edit: I see now your version of the code will not compile if T can be a value type, but the other suggested solution like "if (obj is T) return (T)obj;" will compile. So I see why your suggested alternative will not work, but I don't see why you couldn't use "is".

BlueMonkMN
+3  A: 

Yes you can use your as operator code there instead of the original code, so long as T is a reference type or nullable.

as is the recommended way of casting in C# (see item 3 of Effective C#, by Bill Wagner)

From system.type.isassignablefrom:

[returns] true if c and the current Type represent the same type, or if the current Type is in the inheritance hierarchy of c, or if the current Type is an interface that c implements, or if c is a generic type parameter and the current Type represents one of the constraints of c. false if none of these conditions are true, or if c is null.

From 7.10.11 of the C# spec:

In an operation of the form E as T, E must be an expression and T must be a reference type, a type parameter known to be a reference type, or a nullable type

So you can see that they do comparable checks.

Matt Ellen
This doesn't compile if T is not constrained to be a class
BlueMonkMN
@BlueMonkMN: yea, class or nullable
abatishchev
That'll teach me for not reading the OP properly. Edited
Matt Ellen
+1  A: 

You're probably looking for the is keyword, with the syntax expression is type

Documentation describes it as performing the checks you want:

An is expression evaluates to true if both of the following conditions are met:

• expression is not null.

• expression can be cast to type. That is, a cast expression of the form (type)(expression) will complete without throwing an exception.

Edit However, if instead of just working out whether you can cast something before you try, the as keyword is probably your best solution as you describe in your post.

The following code would perform the same function though...

try
{
    T result = (T)obj;
    return result;
}
catch (InvalidCastException ex)
{
     // throw your own exception or deal with it in some other way.
}

Which method you prefer is up to you...

Fiona Holder