paiden/Nett

Polymorphic table array

queil opened this issue · 3 comments

queil commented

Hi @paiden,

I was trying to do something like the following:

[[item]]
type="A"

[[item]]
type = "B"

And then:

cfg.ConfigureType<IMyCommonInterface>(t =>
                    t.CreateInstance(() =>  /* here I'd resolve an instance from a container based on the "type" property of my table */  )

The problem is that the factory method lacks a TOML context (compare to WithConversionFor<>. FromToml). It this scenario supported at all?

Thanks!

Definitely not a use case I had in mind when creating the lib....

But you can get something to work with the WithConversionFor<> method, see my example below. Why do you try to use the CreateInstance method? Probably because of the bug - oh man always another one - I describe after my example 😆 ...

CreateInstance is only meant to tell 'Nett' how to create a instance of a class without default constructor, nothing else.

This example shows what will work in the near future:

    public sealed class Polymorphism
    {
        public abstract class B
        {
            public string Type { get; set; }

            public B(string t)
            {
                this.Type = t;
            }
        }

        public class C : B
        {
            public C()
                : base(nameof(C))
            {
            }
        }

        public class D : B
        {
            public D()
                : base(nameof(D))
            {
            }
        }

        public class Root
        {
            public List<B> Items { get; set; } = new List<B>();
        }

        [Fact]
        public void FooPol()
        {
            var write = new Root()
            {
                Items = new List<B>()
                {
                    new C(),
                    new D(),
                }
            };

            var t = Toml.WriteString(write);

            var config = TomlSettings.Create(cfg => cfg
                .ConfigureType<B>(tc => tc
                    .WithConversionFor<TomlTable>(c => c
                        .FromToml(tbl => PolyCreate(tbl)))));

            var read = Toml.ReadString<Root>(t, config);

            B PolyCreate(TomlTable table)
            {
                if (table.Get<string>(nameof(B.Type)) == nameof(C))
                {
                    return new C();
                }
                else if (table.Get<string>(nameof(B.Type)) == nameof(D))
                {
                    return new D();
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        }

Unfortunately this example currently does not work out of the box because in TomlTable.Get (~line 170)
the order of get converter and get activated instance seems to be reversed. So the converter will
not be used and a InvalidOperationException will be thrown.
Will have to investigate a little more and create a fix.

But the following workaround should make the example work without the fix in place

var config = TomlSettings.Create(cfg => cfg
    .ConfigureType<B>(tc => tc
        .CreateInstance(() => new C()) // Workaround for bug, should have no effect
        .WithConversionFor<TomlTable>(c => c
            .FromToml(tbl => PolyCreate(tbl)))));
queil commented

@paiden Cool, thanks. Much appreciated. I'll give it a try.

Released with 0.11.0