简体   繁体   English

C#编程代码优化

[英]c# programming code optimization

I have about 20 classes which are derived from ConvertApi classes. 我有大约20个类是从ConvertApi类派生的。 Every class share Convert method from parent class and has unique property SupportedFiles . 每个类都共享父类的Convert方法,并具有唯一的属性SupportedFiles I use these classes for file manipulation and my code looks like 我使用这些类进行文件操作,我的代码看起来像

if (fileEx=="txt")
   new Text2Pdf().Convert(file)
else
  if (fileEx=="doc")
    new Word2Pdf().Convert(file)
     else
    //and so on..

I know that these code can be optimized because 20 times if operator is repeated and that looks bad, but can't find a way to do that. 我知道可以对这些代码进行优化,因为if重复操作符20次,这看起来很糟糕,但是找不到解决方法。 Could anyone help me? 有人可以帮我吗?

class Text2Pdf : ConvertApi
{
  enum SupportedFiles { txt, log }; 
}

class Word2Pdf : ConvertApi
{
  enum SupportedFiles { doc, docx }; 
}

class Excel2Pdf : ConvertApi
{
  enum SupportedFiles { xls, xlsx }; 
}

class ConvertApi
{
 public void Convert(....);
}

In your base class, have something like this: 在基类中,具有以下内容:

public abstract class ConvertApi
{
    protected abstract string[] SupportedFilesImpl();

    public bool Supports(string ext)
    {
        return SupportedFilesImpl.Contains(ext);
    }
}

Now, your derived classes can implement this method: 现在,您的派生类可以实现此方法:

public class Text2PDF : ConvertApi
{
    protected override string[] SupportedFilesImpl { return new string[] { "txt", "log"}; }
}

public class Doc2PDF : ConvertApi
{
    protected override string[] SupportedFilesImpl { return new string[] { "doc", "docx"}; }
}

... and so on for the rest of the converters. ...其余的转换器等等。 Then put these in a list... 然后将它们放在列表中...

List<ConvertApi> converters = new List<ConvertApi>();
converters.Add(new Text2PDF());
converters.Add(new Doc2PDF());

(Note, I'd probably have a class containing these rather than just a list, but anyway). (注意,无论如何,我可能都有一个包含这些内容的类,而不只是一个列表)。 Now, to find a converter: 现在,找到一个转换器:

foreach(ConvertApi api in converters)
{
    if(api.Supports(fileExt))
    {
        // woo!
        break;
    }
}

Assuming each converter is stateless, it sounds like you just want a Dictionary<string, ConvertApi> : 假设每个转换器都是无状态的,这听起来就像您只想要Dictionary<string, ConvertApi>

private static readonly Dictionary<string, ConvertApi> ConverterByType =
    new Dictionary<string, ConvertApi>
{
    { "txt", new Text2PdfConverter() },
    { "doc", new Word2PdfConverter() },
    ...
};

...

ConvertApi converter;
if (!ConverterByType.TryGetValue(fileEx, out converter))
{
    // No converter available... do whatever
}
else
{
    converter.Convert(file);
}

(The dictionary initialization will end up creating more converters than you really need for any converter that supports multiple extensions, but that's a different matter.) (字典初始化最终将创建比所需的支持多个扩展的转换器更多的转换器,但这是另一回事。)

If you need a new converter each time, make it a Dictionary<string, Func<ConvertApi>> and populate it as: 如果每次都需要一个新的转换器,请将其设置为Dictionary<string, Func<ConvertApi>>并将其填充为:

{ "txt", () => new Text2PdfConverter() },
{ "doc", () => new Word2PdfConverter() },

... then call the delegate to get the converter when you need it. ...然后在需要时调用委托以获取转换器。

Of course, this puts all the initialization in one place - you may want a way of getting the converters to register the extensions they understand with a "converter provider" of some type. 当然,这会将所有初始化放在一个地方-您可能想要一种使转换器向某种类型的“转换器提供程序” 注册其了解的扩展的方法。

You need to use an abstract factory pattern , here. 您需要在此处使用抽象的工厂模式 All your text converters should derive from a common interface ITextConverter implementing your Convert method. 您的所有文本转换器都应从实现Convert方法的通用接口ITextConverter派生。 The file extension would be a parameter of your factory. 文件扩展名将是您工厂的参数。 Here is a sample below (typed "on the fly", sometimes copy-pasting code from my sources, so there might be typos. The goal here is to give you a general idea for a flexible implementation). 下面是一个示例(键入“即时”,有时从我的源代码中粘贴粘贴代码,因此可能会有错别字。此处的目的是为您提供灵活实现的总体思路)。

public interface IFileConverter
{
    bool Convert(string filePath);
}


public static class FileConverterFactory
{
    public static IFileConverter Create(string extension)
    {
        extension = type.ToUpper();

        Dictionary<string, ConverterConfig> availableConverters = GetConvertersConfig();

        if (!availableConverters.ContainsKey(extension))
            throw new ArgumentException(string.Format("Unknown extenstion type '{0}'. Check application configuration file.", extension));

        ConverterConfig cc = availableConverters[extension];
        Assembly runnerAssembly = Assembly.LoadFrom(cc.Assembly);
        Type converterType = runnerAssembly.GetType(cc.Class);

        IFileConverter converter = (IFileConverter) Activator.CreateInstance(converterType);

        return converter;
    }


    private static Dictionary<string, ConverterConfig> GetConvertersConfig()
    {
        var configs = (Dictionary<string, ConverterConfig>) ConfigurationManager.GetSection("ConvertersConfig");

        return configs;
    }
}


public class ConvertersConfigHandler : IConfigurationSectionHandler
{
    public object Create(object parent, object configContext, XmlNode section)
    {
        Dictionary<string, ConverterConfig> converters = new KeyedList<string, ConverterConfig>();
        XmlNodeList converterList = section.SelectNodes("Converter");

        foreach (XmlNode converterNode in converterList)
        {
            XmlNode currentConverterNode = converterNode;

            ConverterConfig cc = new ConverterConfig();
            cc.Type = XML.GetAttribute(ref currentConverterNode, "Type").ToUpper();
            cc.Assembly = XML.GetAttribute(ref currentConverterNode, "Assembly");
            cc.Class = XML.GetAttribute(ref currentConverterNode, "Class");

            converters[cc.Type] = cc;
        }

        return converters;
    }
}


public class ConverterConfig
{
    public string Type = "";
    public string Assembly = "";
    public string Class = "";
}


public class TextConverter : IFileConverter
{
   bool Convert(string filePath) { ... }
}


public class PdfConverter : IFileConverter
{
   bool Convert(string filePath) { ... }
}

In your app.config file, you add this to the configSections: 在您的app.config文件中,将其添加到configSections中:

<section name = "ConvertersConfig" type = "ConvertersConfigConfigHandler, MyAssembly" />

and this below your configSections: 而这在您的configSections下面:

<ConvertersConfig>
    <Converter Type="txt" Assembly="MyAssembly" Class="MyAssembly.TextConverter" />
    <Converter Type="pdf" Assembly="MyAssembly" Class="MyAssembly.PdfConverter" />
</ConvertersConfig>

The call would then be like this: 调用将如下所示:

IFileConverter converter = FileConverterFactory.Create("txt");
converter.Convert("c:\temp\myfile");

EDITED the code for giving a solution that is more "generic". 编辑了用于提供更“通用”解决方案的代码。

  1. C# supports switch-case operator for strings, ie your code coud be rewritten as C#支持字符串switch-case运算符,即您的代码被重写为

     switch (fileEx) { case "txt" : new Text2Pdf().Convert(file); break; case "doc": new Word2Pdf().Convert(file); break; ... } 
  2. If you change your classes' names to correspond to supported extensions then you will be able to construct them using reflection, like this (error checking omitted for brevity): 如果您将类的名称更改为与支持的扩展相对应,则可以使用反射来构造它们,如下所示(为简便起见,省略了错误检查):

      var t = Type.GetType(fileEx + "2Pdf"); var tConstructor = t.GetConstructor(Type.EmptyTypes); var tInstance = tConstructor.Invoke(new object[0]); ((ConvertApi)tInstance).Convert(...); 

This might require some additional work (ie create a separate class for each extension, deriving them from some base class - for example Doc2Pdf and Docx2Pdf both deriving from Word2Pdf). 这可能需要一些额外的工作(即为每个扩展创建一个单独的类,从某个基类派生它们,例如Doc2Pdf和Docx2Pdf都派生自Word2Pdf)。

The advantage is that you will not have to touch this part of code anymore. 这样做的好处是您不再需要接触这部分代码。 If you're planning to write some interface for plugins then it may come in handy. 如果您打算为插件编写一些接口,那么它可能会派上用场。

The code above also has an assumption that your ConvertApi classes all have default parameterless constuctor. 上面的代码还假设您的ConvertApi类都具有默认的无参数构造函数。

You can use some reflection: 您可以使用一些反射:

ConvertApi FindMatchingConverter(string _FileExt)
        {
            //Get all the converters available.
            List<ConvertApi> converters = this.GetType()
                                           .Assembly
                                           .GetExportedTypes()
                                           .Where(type => type.IsAssignableFrom(typeof(ConvertApi)))
                                           .ToList();

            //Loop and find which converter to use
            foreach (var converter in converters)
            {
                if (converter.SupportedFiles.Contains(_FileExt))
                    return Activator.CreateInstance(converter);
            }
            throw new Exception("No converter found");
        }

Then you just have to call Convert() on the ConvertApi returned. 然后,您只需要在返回的ConvertApi上调用Convert()
Of course, this requires you to add a virtual List<String> named SupportedFiles in your base class. 当然,这要求您在基类中添加一个名为SupportedFiles的virtual List<String>

This makes it look like 这看起来像

public abstract class ConvertApi
{
    public abstract void Convert();

    public virtual List<String> SupportedFiles {get;set;}
}

What about using a switch ? 那使用switch呢?

switch(fileEx){
    case "txt": new Text2Pdf().Convert(file); break;
    case "doc": new Word2Pdf().Convert(file); break;
}

在类的构造函数中传递支持的文件时,请使用依赖项注入。

Slower, but more dynamic... use an Object Factory. 速度较慢,但​​动态性更高...使用对象工厂。 Here's a good article that seems to fit your needs. 这是一篇不错的文章,看来很符合您的需求。

http://www.codeproject.com/Articles/12986/Generic-Object-Factory http://www.codeproject.com/Articles/12986/Generic-Object-Factory

The important stuff from the article: 文章中的重要内容:

using System.Collections.Generic;

public struct Factory < KeyType, GeneralProduct > 
{
    //Register a object with the factory
    public void> Register< SpecificProduct >(KeyType key)
        where SpecificProduct : GeneralProduct, new()
    {
        if( m_mapProducts == null )
                {
            m_mapProducts = new SortedList< KeyType, CreateFunctor >(); 
        }
        CreateFunctor createFunctor = Creator<SpecificProduct>; 
        m_mapProducts.Add(key, createFunctor);
    }

    //Create a registered object 
    public GeneralProduct Create( KeyType key )
    {
        CreateFunctor createFunctor = m_mapProducts[ key ];
        return createFunctor(); 
    }

    private GeneralProduct Creator < SpecificProduct >() 
        where SpecificProduct : GeneralProduct, new()
    {
        return new SpecificProduct();
    }

    private delegate GeneralProduct CreateFunctor(); 

    private SortedList<KeyType, CreateFunctor>  m_mapProducts;
}

Usage: 用法:

class Fruit
{ 
} 

class Apple : Fruit 
{ 
} 

class Orange : Fruit 
{ 
} 

class TestClass
{ 
    static void Main(string[] args) 
    { 
        General.Factory< string, Fruit > factory; 

        //Register
        factory.Register< Apple >( "Apple" ); 
        factory.Register< Orange>( "Orange" ); 

        //Create 
        Fruit fruit1 = factory.Create("Apple"); 
        Fruit fruit2 = factory.Create("Orange"); 
    } 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM