C# 可从或作为识别?

C# 可从或作为识别?,c#,generics,inheritance,interface,C#,Generics,Inheritance,Interface,我有下一个代码: private T CreateInstance<T>(object obj) // where T : ISomeInterface, class { ... if (!typeof(T).IsAssignableFrom(obj.GetType())) { throw ..; } return (T)obj; } 如果没有-为什么?如果(!(条为T)){throw.;} 或者,如果您不需要自己的异常消息,最简单的答案就是: retu

我有下一个代码:

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

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

    return (T)obj;
}
如果没有-为什么?

如果(!(条为T)){throw.;}

或者,如果您不需要自己的异常消息,最简单的答案就是:

return (T)obj;

如果它不可强制转换,则会抛出InvalidCastException并忽略返回。除非您添加了更多的逻辑或自定义错误消息,否则无需进行检查并抛出您自己的异常。

甚至更好,因为它更容易读取真正的条件

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

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

当你把“是”和“是”放在一起时,原因可能是这样的(括号越少,可读性越好)

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

if (obj is T)
而不是

if (!(obj is T))
所以最终版本可以是

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

throw new ...


class约束
,其中T:class
允许您将
用作T
语句

private T CreateInstance<T>(object obj) where T : class
{
    if (!(obj is T)) { throw new ArgumentException("..."); }
    return obj as T;
}
private T CreateInstance(object obj),其中T:class
{
如果(!(obj是T)){抛出新的ArgumentException(“…”);}
将obj返回为T;
}

private T CreateInstance(对象obj)
{
如果(!(obj是T)){抛出新的ArgumentException(“…”);}
返回(T)obj;
}
另一种变体:

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;
}
private T CreateInstance(object obj),其中T:isome接口//如上所述
{
...
T结果=obj作为T;
如果(结果==null)
{扔..;}
其他的
返回结果;
}

它可能用于处理转换构造函数允许该操作的情况,但显然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为null时抛出异常。这是我能看到的唯一区别。当obj为null时,obj.GetType()将引发null引用异常,而不是引发指定的异常


编辑:我现在看到,如果T可以是值类型,那么您的代码版本将不会编译,但是其他建议的解决方案,如“if(obj是T)return(T)obj;”将编译。因此,我明白为什么您建议的替代方案不起作用,但我不明白为什么您不能使用“is”。

是的,只要t是引用类型或可为null,您可以在那里使用
作为
运算符代码,而不是原始代码

as
是推荐的C#铸造方法(见比尔·瓦格纳的《有效C#》第3项)

发件人:

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

根据C规范第7.10.11节:

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


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

您可能正在查找
is
关键字,语法为
表达式is type

将其描述为执行所需的检查:

is表达式的计算结果为true,如果 以下两种情况均适用 会议:

•表达式不为空

•表达 可以转换为类型。就是演员阵容 形式的表达 (类型)(表达式)将完成 没有抛出异常

编辑 然而,如果在你尝试之前不只是确定你是否可以投下一些东西,as关键字可能是你在文章中描述的最好的解决方案

下面的代码将执行相同的功能

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

您喜欢哪种方法取决于您……

此场景使用的是可识别的:

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

只针对喜欢玩数字游戏的开发者(谁不喜欢!)

在下面,您会发现的性能比较测试可从As中识别。当然,只有当您有一个实例时,这才算

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

IsAssignableFrom:经过146毫秒

[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);
}
A操作员:运行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);
}
[TestMethod]
public void可从VSAsperformanceTest()中签名
{
秒表秒表=新秒表();
秒表。开始();
int尝试次数=1000000次;
string value=“这是一个测试”;
for(int-trunt=0;trunt
酒吧是从哪里来的?你修复了。。。您将bar作为T…如果您无论如何都要抛出,为什么不直接
return(T)obj
@Anton:我只会在obj为T的情况下返回,否则我会抛出一个例外你已经参与了同样的讨论:)我想他可能需要添加一些约束,但这不是他想要实现的吗?如果T可以是这种类型。。。铸造它?可惜它无法编译。你试过这个吗?为什么你不想在施放之前检查类型?@Daniel:没有类它是不可编译的constraint@Daniel戴森:当然不,“扔…”是C#不允许的compiler@Orsol,原始答案没有类约束(其中t:…)。我可以在我的cod中添加一个新的异常(“”)
try
{
    T result = (T)obj;
    return result;
}
catch (InvalidCastException ex)
{
     // throw your own exception or deal with it in some other way.
}
foreach (PropertyInfo property in GetType().GetProperties())
{
    if (typeof(SubPresenter).IsAssignableFrom(property.PropertyType))
    {//Do Sth.}
}
[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);
}