abatishchev
abatishchev

Reputation: 100268

IsAssignableFrom or AS?

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?

Upvotes: 5

Views: 3211

Answers (11)

Roel van Megen
Roel van Megen

Reputation: 341

Just for the developers who like to play the numbers game (who doesn't!).

Below you'll find a performance comparison test for IsAssignableFrom vs. As. Of course this will only count if you have an instance.

The result of the test (one million attempts):

IsAssignableFrom: 146 ms elapsed

AsOperator: 7 ms elapsed

[TestMethod]
public void IsAssignableFromVsAsPerformanceTest()
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    int attempts = 1000000;
    string value = "This is a test";

    for (int attempt = 0; attempt < attempts; attempt++) {
        bool isConvertible = typeof(IConvertible).IsAssignableFrom(value.GetType());
    }

    stopwatch.Stop();
    Console.WriteLine("IsAssignableFrom: {0} ms elapsed", stopwatch.ElapsedMilliseconds);

    stopwatch.Restart();

    for (int attempt = 0; attempt < attempts; attempt++) {
        bool isConvertible = value as string != null;
    }

    stopwatch.Stop();
    Console.WriteLine("AsOperator: {0} ms elapsed", stopwatch.ElapsedMilliseconds);
}

Upvotes: 1

IvanYu
IvanYu

Reputation: 11

IsAssignableFrom used by this scene:

foreach (PropertyInfo property in GetType().GetProperties())
{
    if (typeof(SubPresenter).IsAssignableFrom(property.PropertyType))
    {//Do Sth.}
}

Upvotes: 1

David Ly
David Ly

Reputation: 31596

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.

Upvotes: 6

desco
desco

Reputation: 16782

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 ...
}

Upvotes: 2

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...

Upvotes: 1

Daniel Dyson
Daniel Dyson

Reputation: 13230

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;
}

Upvotes: 1

garik
garik

Reputation: 5756

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..

Upvotes: 1

Matt Ellen
Matt Ellen

Reputation: 11592

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.

Upvotes: 3

BlueMonkMN
BlueMonkMN

Reputation: 25601

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".

Upvotes: -2

Denis Palnitsky
Denis Palnitsky

Reputation: 18387

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;
}

Upvotes: 4

Nix
Nix

Reputation: 58522

Or even better because its easer to read true conditionals.

 if(obj is T){
    //Create instance. 
 }
 else{
    throw new InvalidArgumentException("Try Again");
 }

Upvotes: -3

Related Questions