Home > Net >  How to specify properties of an object as optional/required?
How to specify properties of an object as optional/required?

Time:07-22

I'm trying to find the best way to tag properties of an object as required/optional/ignored.

public enum classtype
{
    A,
    B,
    C,
    D
}
class example
{
    public classtype type { get; set; }
    public string a { get; set; }
    public string b { get; set; }
    public string c { get; set; }
    public int d { get; set; }
}

What im doing so far:

public static void DoSomething(List<example> examples)
        {
            foreach(example ex in examples)
            {
                DoSomethingElse(ex.d);
                if(ex.type == classtype.A)
                {
                    DoSomethingElse(ex.b);
                    DoSomethingElse(ex.a);
                }
                else if (ex.type == classtype.B)
                {
                    DoSomethingElse(ex.b);
                    DoSomethingElse(ex.c);
                }
                ...
            }
        }

What i want is something like this:

public static void DoSomething(List<example> examples)
        {
            foreach (example ex in examples)
            {
                foreach (PropertyInfo prop in ex.GetType().GetProperties())
                {
                    if (prop.isRequired)
                    {
                        DoSomethingElse(prop);
                    }
                    else if (prop.isOptional)
                    {
                        DoThis(prop);
                    }
                    ...
                }
            
            }
        }

i.e.

  • -type.A: a.req, b.opt, c.req, d.ign
    -type.B: a.ign, b.req, c.opt, d.req
    -...

The goal is to have a way to iterate over the objects and their properties.

I'm thinking to use a dictionary to define types, but that doesn't strike me as the most efficient way to implement this.

CodePudding user response:

You could try using attributes.



class Example
{
    [UI(Type = UIType.Required)]
    public string a { get; set; }

    [UI(Type = UIType.Required)]
    public string b { get; set; }
}

then you can

PropertyInfo property = ...
var uis = property.GetCustomAttributes(typeof(UIAttribute), inherit: false) as UIAttribute[];
if (uis == null) ...
var ui = ui[0];
switch a 
{
   UIType.Required => ...
   ...
}

CodePudding user response:

The way that I would solve this is by making use of the Strategy Pattern

I'd transform your Example into something like:

public abstract class Example
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
    public int D { get; set; }

    public abstract void DoSomething();
}

The DoSomething method is where we want to do our heavy lifting. Derived classes can be created like the rules that was originally there, for example:

public class ExampleA : Example
{
    public override void DoSomething()
    {
        Console.WriteLine("Doing something A");
    }
}

public class ExampleB : Example
{
    public override void DoSomething()
    {
        Console.WriteLine("Doing something B");
    }
}

And now your code only has to do:

public static void DoSomething(List<Example> examples)
{
    foreach(Example ex in examples)
    {
        ex.DoSomething();
    }
}

If you're wanting to create Examples via your ClassType enum, you can create it via a factory like:

public class ExampleFactory
{
    private readonly List<Type> types = new();

    public ExampleFactory()
    {
        types.AddRange(Assembly.GetAssembly(typeof(Example))
                                ?.GetTypes()
                                .Where(t => t.IsSubclassOf(typeof(Example))) ?? Type.EmptyTypes);
    }
    
    public Example Create(ClassType type)
    {
        var typeToCreate = types.FirstOrDefault(t => t.Name.EndsWith(type.ToString()));
        if (typeToCreate == null)
        {
            throw new ArgumentException($"No type found for {type}");
        }
        return (Example)Activator.CreateInstance(typeToCreate)!;
    }
}

Which can be called like:

// Only create once
ExampleFactory factory = new ExampleFactory();
var example = factory.Create(ClassType.A);

The factory works based on a naming convention, where it has to have the suffix of the Class Type at the end (i.e. ExampleA), but this can be anything you want. You can modify the factory to accept constructor parameters.

If you're wanting to be adventurous (let me know if you want to see an example of this), you can change the factory to be source generated.

  • Related