IsAssignableFrom或AS?

5

我有以下代码:

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

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

    return (T)obj;
}

可以用这个替换吗?
T result = obj as T;

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

return result;

如果不行 - 为什么?

你修复了......你将 bar 定义为 T.... - Nix
@Anton:只有当obj是T类型时,我才会返回;否则我会抛出一个异常。 - abatishchev
你已经参与了相同的讨论 https://dev59.com/THRB5IYBdhLWcg3wN1AQ#686523 :) - garik
@igor:也许删除那篇帖子可以获得“同侪压力”徽章? :)) - abatishchev
如果 obj 不是 T,则 (T)obj 将抛出异常。 - Anton Tykhyy
显示剩余4条评论
11个回答

6

如果 (!(bar is T)) { throw ..; } 这段代码的意思是如果 bar 不是类型 T,则抛出异常。

或者,如果您不需要自己的异常消息,最简单的方法就是执行以下操作:

return (T)obj;

如果不可转换,将抛出 InvalidCastException 异常并忽略返回值。除非您添加了更多逻辑或自定义错误消息,否则没有必要进行检查并抛出自己的异常。

谢谢,我认为这种编程方式(守卫条款模式)更好。 - Ozkan

4

另一种变体:

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

真可惜它无法编译。你试过这个吗? - Daniel Dyson
为什么你不想在进行强制类型转换之前检查类型呢? - Nix
@Orsol,原始答案没有类约束(其中T:...)。我能够在我的代码中添加一个新的Exception(""),但还是感谢您的想法。当然,它仍然无法编译,因为编辑中添加的约束是接口约束,而不是类约束。如果可以的话,我会投两次反对票。 :) - Daniel Dyson
@Orsol:非常遗憾,你的回答得到了如此多的负面评价。这是不应该的! - abatishchev
@Orsol:有个蠢货来了,把整篇帖子都点了个踩! - abatishchev
显示剩余4条评论

3

当T是引用类型或可空类型时,您可以在那里使用as运算符代码替代原始代码。

as是C#中推荐的转换方式(请参见Bill Wagner的Effective C#第3项)。

来自system.type.isassignablefrom

[返回]true,如果c和当前Type表示相同的类型,或者当前Type在c的继承层次结构中,或者当前Type是c实现的接口,或者c是泛型类型参数并且当前Type表示c的约束之一。如果没有这些条件为真或c为空,则返回false。

从C#规范的7.10.11节中:

在E as T形式的操作中,E必须是一个表达式,T必须是引用类型、已知为引用类型的类型参数或可空类型

因此,您可以看到它们进行了类似的检查。


如果T没有被限制为类,则此代码无法编译。 - BlueMonkMN
@BlueMonkMN:是的,类或可空。 - abatishchev

2
也许这样写会更易读(少用括号,提高可读性)。
if (obj is T)
{
    return (T)obj;
}
else
   throw new ...

编辑 通过减少括号的数量,我最初意思是指反向检查:即

if (obj is T)

代替
if (!(obj is T))

所以最终版本可以是:
if (obj is T)
{
    return (T)obj;
}

throw new ...

或者

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

更容易出错,因为在 throw 语句后插入另一行代码似乎是有点合理的。 - Joel Mueller

1
类约束 where T : class 允许您使用 as T 语句。
private T CreateInstance<T>(object obj) where T : class
{
    if (!(obj is T)) { throw new ArgumentException("..."); }
    return obj as T;
}

或者

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

除了最好使用isas之外,一切都正确。双重操作没有意义。 - abatishchev
真的。我更新了我的答案以反映这一点。谢谢@abatishchev - Daniel Dyson
MSDN推荐如下:T result = obj as T; if (result != null ) { } else { }。就像@Orsol的答案一样。或者你的答案——如果没有: class约束。 - abatishchev
是的,所有这些都可以正常工作。看起来你可能已经回答了自己的问题。 - Daniel Dyson

1

您可能正在寻找使用语法expression is typeis关键字。

文档将其描述为执行所需检查的内容:

如果满足以下两个条件,则is表达式的结果为true:

• expression不为null。

• expression可以强制转换为type。也就是说,形如(type)(expression)的强制转换表达式将会成功完成而不会抛出异常。

编辑然而,如果您只是想在尝试转换之前确定是否可以转换,那么as关键字可能是最好的解决方案,正如您在帖子中所描述的那样。

以下代码将执行相同的功能...

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

你喜欢哪种方法就用哪种方法...


1

此场景使用了IsAssignableFrom:

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

1

这是给喜欢玩数字游戏的开发者们准备的(谁不喜欢呢!)。

下面是一个 IsAssignableFromAs 的性能比较测试。当然,这只有在你有实例的情况下才会生效。

测试结果(一百万次尝试):

IsAssignableFrom:经过146毫秒

AsOperator:经过7毫秒

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

1

这篇文章

第二个是安全的...因为在第一个中,如果obj为null,你将会得到异常(obj.GetType() --> NullReferenceException)。

当你先使用"is"然后使用"as"时会导致 性能问题..


我认为他可能需要添加一些约束条件,但这不是他试图完成的目标吗?如果T可以是这种类型...进行转换? - Nix

-2

这可能是为了处理转换构造函数允许操作的情况,但显然IsAssignableFrom也无法处理。我没有看到任何可以处理这种情况的东西。所以我不知道如何检查这种情况:

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

最后,我不明白为什么你不想使用你的代码版本,除非你希望当obj为空时抛出异常。这是我能看到的唯一区别。 obj.GetType()将在obj为空时抛出空引用异常,而不是抛出指定的异常。 编辑:现在我看到如果T可以是值类型,你的代码版本将无法编译,但其他建议的解决方案如“if(obj is T)return(T)obj;”将会编译。所以我明白了你提出的替代方案为什么行不通,但我不明白为什么你不能使用“is”。

我不明白为什么人们会在没有解释问题的情况下投票反对答案。这有什么意义呢?如果没有人解释问题,我们就无法从反对票中学到任何东西。 - BlueMonkMN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接