简体   繁体   中英

C#: Create instance of a type based on an integral Enum value

I have an interface for implementing an "output formatter" that looks a bit like this:

public interface IFormatOutput {}
public class HtmlOutputFormatter : IFormatOutput {}
public class TextOutputFormatter : IFormatOutput {}
// etc, etc...

public enum OutputFormat {
    Html,
    Text,
    HappyMeal,
    Excel
}

public class SomeFormattableEntity {
    int Id { get; set; }
    OutputFormat OutputType { get; set; }
}

So SomeFormattableEntity is persisted in a database via Dapper and its OutputType property is stored as the underlying integer value (ie, in an INT column). As you can guess, I want to provide an instance of a IFormatOutput to handle a SomeFormattableEntity based on its OutputType property.

Is there some clean best-practice way to handle this type of relationship? My ideas so far include a factory with innards potentially consisting of:

  1. grandpa's horrible ugly switch statement
  2. an array mapping the enum value to a Type
  3. reflection-based magic mapping enum member name as string to class type elsewhere
  4. some mapping mechanism involving attributes

I realize it is not desirable to require an instance of a thing whose type is based on a value, but it seems hard to avoid this when SQL is involved. Basically the problem is that multiple "things" that all have varying .NET types are stored in a single table. I keep running into this idiom and am unable to find an elegant solution to it.

How about:

OutputFormat format = OutputFormat.Excel;
object obj = Activator.CreateInstance("myAssemblyName", format.ToString());

Assuming the elements of your enum has the exact name of your types?

I'd probably go for a custom attribute with a FormatsOutputFor property. Then decorate all of your implementations of IFormatOutput with the attribute. eg

[YourAttribute(OutputFormat.Html)]
public class HtmlOutputFormatter : IFormatOutput {}

Then in your factory:

// get all your formatters
var formatters = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => Attribute.IsDefined(p, typeof(YourAttribute)));

// Now go through each formatter and use the attribute to figure out which
// output format it's for. Add these to some static IDictionary<OutputFormat, Type>

You probably want to build some internal cache that maps an OutputFormat value to a Type . Then your factory can double check that you have only got one type mapped to each output format and if you try to get a formatter for an enum value that doesn't have a corresponding class then you wont get some obscure TypeLoadException from activator.

Hopefully that makes sense...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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