首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通过ID (或名称)创建对象

通过ID (或名称)创建对象
EN

Stack Overflow用户
提问于 2012-06-08 02:52:28
回答 5查看 205关注 0票数 3

我有一个很多子类都继承的抽象类:

代码语言:javascript
运行
复制
  public abstract class CrawlerBase
    {
        public abstract void Process(string url);
    }

我正在处理这个循环:

代码语言:javascript
运行
复制
foreach (var item in result)
                {
                    object crawler = null;

                    switch (item.Type)
                    {
                        case "Trials":
                            var t = new Trials(); 
                            ct.Process(item.URL); //repetitive code.  
                            break;

                        case "Coverage":
                            var c = new Coverage();
                            c.Process(item.URL); //repetitive code.
                            break;
                        default:
                            break;
                    }

                   // crawler.Process(item.URL);
                }

现在,item.type字符串将取决于需要实例化的子类。因为我的所有子类都继承了我的基类,所以在每个case语句中调用.Process()都是非常重复的。我希望将对象"crawler“转换为被实例化的子类,并在switch语句的末尾调用crawler.Process(),如注释中所示。我该怎么做呢?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-06-08 02:59:24

有许多方法可以从字符串创建类型。反射可以是其中之一,它是(但如果你正在处理web请求,你可能不会关心这一点),但你不需要保留一长串硬编码的字符串和/或有一个丑陋的长开关/用例语句。

如果速度不是问题,那么让我们从一些简单的东西开始:

代码语言:javascript
运行
复制
CrawlerBase crawler = (CrawlerBase)Activator.CreateInstance(
    Type.GetType("MyNamespace." + item.Type));

crawler.Process(item.URL);

使用反射,你不需要开关/大小写,并且,不管你有多少类型,你不会改变你的工厂代码来适应CrawlerBase的新实现。只需添加一个新的实现即可。

它是如何工作的?您可以从类的Type定义(使用Activator.CreateInstance)创建一个类的实例,所以问题在于获取TypeType类提供了一个静态GetType方法,可用于从(完整)名称创建一个Type。在这种情况下,我们提供了一个名称空间,必须将其更改为真实的名称空间,如果您将所有类都放在同一个名称空间中,您可以编写如下内容:

代码语言:javascript
运行
复制
string rootNamespace = typeof(CrawlerBase).Namespace;

然后是虚构的版本:

代码语言:javascript
运行
复制
CrawlerBase crawler = (CrawlerBase)Activator.CreateInstance(
    Type.GetType(rootNamespace + "." + item.Type));

crawler.Process(item.URL);

改进1

这是一个非常幼稚的实现,一个更好的版本应该在字典中缓存Type

代码语言:javascript
运行
复制
private static Dictionary<string, Type> _knownTypes =
    new Dictionary<string, Type>();

private static GetType(string name)
{
    string fullName = typeof(CrawlerBase).Namespace
        + "." + name;

    if (_knownTypes.ContainsKey(fullName))
        return _knownTypes[fullName];

    Type type = Type.GetType(fullName);
    _knownTypes.Add(fullName, type);

    return type;
}

现在你已经很好了,你不需要管理一个(可能的)很长的字符串列表,如果你添加/删除/更改一个类型,你不需要改变无尽的开关/大小写,并且添加一个新的Crawler,你所要做的就是派生一个新的类。请注意,此函数不是线程安全的,因此,如果您有多个线程,则必须添加一个锁来保护字典访问(或ReadWriterLockSlim,这取决于您的使用模式)。

改进2

下一步是什么?如果您的要求更严格(例如,关于安全性),则可能需要添加更多内容(我认为这不是您的情况,因此您甚至可能不需要类型字典)。还能做些什么?首先,您可以检查创建的类型是否派生自CrawlerBase (使用IsAssignableFrom并提供更有意义的异常,而不是InvalidCastException);其次,您可以使用属性来修饰您的类。只有具有该属性的类才会被创建(因此您可以保留私有实现,即使标记为public也不能从您的用户创建)。不仅如此,一个简单的实现通常就足以解决这类问题(当输入不是来自用户,而是来自健壮的配置文件或程序本身时)。

票数 2
EN

Stack Overflow用户

发布于 2012-06-08 02:58:04

这不管用吗?

代码语言:javascript
运行
复制
foreach (var item in result)
{
    CrawlerBase crawler = null;

    switch (item.Type)
    {
        case "Trials":
            crawler = new Trials(); 
            break;
        case "Coverage":
            crawler = new Coverage();
            break;
        default:
            break;
    }

    if(crawler != null)
        crawler.Process(item.URL);
}
票数 4
EN

Stack Overflow用户

发布于 2012-06-08 02:59:02

使用工厂方法创建字典:

代码语言:javascript
运行
复制
var factories = new Dictionary<string, Func<CrawlerBase>>
{
  {"Trials", () => new Trials()},
  {"Coverage", () => new Coverage()},
// and so on
}

...and丢弃switch

代码语言:javascript
运行
复制
foreach (var item in result)
{
  factories[item.Type]().Process(item.URL);
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10938067

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档