【问题标题】:Find class with a specific property value - and then create instance of this class查找具有特定属性值的类 - 然后创建此类的实例
【发布时间】:2013-06-06 06:54:51
【问题描述】:

我有以下班级样本:

public class ExceptionOne : BaseException
{
    //Define the error bit the exception represents
    public static int ErrorBitNumber = 1234;

    public ExceptionOne()
    {
        //Do something in  the ctor
    }
}

(BaseException 中没有相关功能,因此无需显示代码;))

还有其他异常类,其属性名为ErrorBitNumber,但属性ErrorBitNumber 具有其他值。每个异常类代表一个错误位编号 - 因此一个错误位编号总是有一个类别。

因为我永远不知道我收到了哪个错误位号(如果我收到一个)我想实现以下 -->

  • 遍历从BaseException 派生的每个现有类,并查找接收到的错误位数
  • 如果找到类,则创建特定类的实例(填充一些其他属性或其他内容
  • 扔了

我知道这应该可以通过使用反射来实现 - 但我真的不知道如何。此外,我认为使用ErrorBitNumber 作为public static 应该是正确的方法。如果没有,请随时纠正我。

更新 1:

为了理解:我们有将被解析的头文件。在此头文件中定义了错误。对于每个错误,将创建一个具有特定 errorbitnumber 的异常类。因此,在架构的最低层上,我收到一个错误位数,并且必须抛出代表特定错误位数的特定异常

【问题讨论】:

  • 不要为此寻找反射黑客。此处使用的正确模式是工厂模式。简单干净。

标签: c# class inheritance reflection


【解决方案1】:

假设所有相关类都直接派生自BaseException,并且与它在同一个程序集中,您可以使用以下代码:

var exceptionType =
    typeof(BaseException)
        .Assembly.GetTypes()
        .Where(x => x.BaseType == typeof(BaseException))
        .Select(x => new { ExceptionType = x, Property = x.GetProperty("ErrorBitNumber", BindingFlags.Public | BindingFlags.Static) })
        .Where(x => x.Property != null)
        .FirstOrDefault(x => x.Property.GetValue(null, null) == errorBitNumber)
        .ExceptionType;

if(exceptionType == null)
    throw new InvalidOperationException("No matching exception has been found");

var exception = (BaseException)Activator.CreateInstance(exceptionType);
throw exception;

这应该可以,但我永远不会使用它。我会创建某种异常注册表,可用于检索特定错误位的新异常实例。

异常注册表可以通过多种方式实现,主要取决于您的具体需求。最通用和最灵活的方法是简单地注册工厂:

public class ExceptionRegistry<TKey, TExceptionBase> where TExceptionBase : Exception
{
    private readonly Dictionary<TKey, Func<TExceptionBase>> _factories = new ...;

    public void Register(TKey key, Func<TExceptionBase> factory)
    {
        _factories[key] = factory;
    }

    public TExceptionBase GetInstance(TKey key)
    {
        Func<TExceptionBase> factory;
        if(!_factories.TryGetValue(key, out factory))
            throw new InvalidOperationException("No matching factory has been found");
        return factory();
    }
}

用法如下:

var exception = registry.GetInstance(errorBitNumber);
throw exception;

因为它是最灵活的方法,所以在实际注册异常类方面也是最冗长的方法:

var registry = new ExceptionRegistry<int, BaseException>();
registry.Register(ExceptionOne.ErrorBitNumber, () => new ExceptionOne());
registry.Register(ExceptionTwo.ErrorBitNumber, () => new ExceptionTwo());
registry.Register(ExceptionThree.ErrorBitNumber, () => new ExceptionThree());

您基本上必须手动注册每个异常类。但是,这样做的好处是您可以自定义异常的创建:

registry.Register(ExceptionFour.ErrorBitNumber,
                  () => new ExceptionFour(some, parameters));

如果您不想为每个异常类创建手动注册,您可以结合两种方法:
您仍然会使用反射来获取所有异常类。但结果将用于填充注册表,以便您可以使用注册表实际检索实例。以这种方式使用反射来创建注册表基本上是“约定优于配置”。这里最大的优势是您只执行一次注册,基本上成为基础设施代码。之后,您就有了一个定义明确的接口 - 注册表 - 您可以使用。

它可能看起来像这样:

var registry = new ExceptionRegistry<int, BaseException>();
var exceptions = 
    typeof(BaseException)
        .Assembly.GetTypes()
        .Where(x => x.BaseType == typeof(BaseException))
        .Select(x => new { ExceptionType = x, Property = x.GetProperty("ErrorBitNumber", BindingFlags.Public | BindingFlags.Static) })
        .Where(x => x.Property != null)
        .Select(x => new { Key = (int)x.Property.GetValue(null, null)
                           Factory = (Func<BaseException>)(() => Activator.CreateInstance(x.ExceptionType)) });
 foreach(var exception in exceptions)
     registry.Register(exception.Key, exception.Factory);

实际获取异常实例的用法将使用注册表:

var exception = registry.GetInstance(errorBitNumber);
throw exception;

【讨论】:

  • 谢谢,这正是我一直在寻找的方式——但“异常注册表”听起来也很有趣。您能否告诉我创建异常注册表是什么意思。也许它比使用反射更好。
  • 哇,我喜欢注册表的方法。感谢这两种方法的结合,因为总是会发生有人添加新的例外 - 所以没有必要手动将它们添加到注册表(有关解释,请参阅主要问题的更新)。谢谢!
  • 好像我在(Func&lt;BaseCanUnitErrorException&gt;)() =&gt; Activator.CreateInstance(x.ExceptionType) }); 缺少一些东西来自) =&gt; Activator.CreateInstance(x.ExceptionType) }); 它带有红色下划线
  • @DanielW.:试着把它放在括号里:(Func&lt;BaseCanUnitErrorException&gt;)(() =&gt; Activator.CreateInstance(x.ExceptionType) }));
猜你喜欢
  • 2021-05-19
  • 2010-10-17
  • 1970-01-01
  • 2013-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-21
相关资源
最近更新 更多