Mixin Copy Value

Hello everyone,

I want to write a bit more complex mixin and for this I would need to receive all changes of a member that the mixin is on. For testing I wanted to write a simple mirror mixin, where my only goal was to have the same value as the value of the member I would apply the mixin on (at all time so basically whenver it changes). I could not really figure out a clean way to do this though. I appreciate any help or guidance to the correct approach on how to do this.

Hi @mavo1,

Do you need it to react to the changing value? Otherwise you can mirror the property value by creating a subclass of the MixinMemberData class and override GetValue. GetValue should then call the original member. SetValue could set the value of the original member.

This would effectively mirror that other property.

It is more difficult to get a callback when the value of a member has changed, although it can be done with some limitations.

Thank you so much for your answer. I got it to work now with a subclass of MixinMemberData. Here I have the code. Could you just take a quick look at it if this is what you meant?

internal sealed class TestMixinMemberData : MixinMemberData
{
    private readonly string _targetMemberName;
    private readonly ConditionalWeakTable<object, TestMixin> _mixinInstances = new ConditionalWeakTable<object, TestMixin>();

    public TestMixinMemberData(
        IMixinBuilder builder,
        string targetMemberName) : base(builder)
    {
        _targetMemberName = targetMemberName ?? throw new ArgumentNullException(nameof(targetMemberName));
    }

    public override object GetValue(object owner)
    {

        if (owner == null) return null;


        if (!_mixinInstances.TryGetValue(owner, out var mixinInstance))
        {
            mixinInstance = new TestMixin(_targetMemberName);
            _mixinInstances.Add(owner, mixinInstance);

            if (owner is ITestStep testStep)
            {
                mixinInstance.SetOwner(testStep);
            }
        }

        return mixinInstance;
    }

    public override void SetValue(object owner, object value)
    {
        if (owner == null) return;

        if (value is TestMixin mixin)
        {
            // Always update the target member name to match this MixinMemberData.
            // This is critical when the mixin is modified via "Modify Mixin..."
            if (mixin.TargetMemberName != _targetMemberName)
            {
                mixin.TargetMemberName = _targetMemberName;
            }

            // Remove old instance if exists
            _mixinInstances.Remove(owner);
            _mixinInstances.Add(owner, mixin);

            if (owner is ITestStep testStep)
            {
                mixin.SetOwner(testStep);
            }
        }
    }
}

Actually, I am not sure. It depends a bit on the broader context.

I pictured you want something like this:

    [MixinBuilder(typeof(object))]
    class TestMixinBuilder : ValidatingObject, IMixinBuilder
    {
        public string MemberTargetName { get; set; }
        public void Initialize(ITypeData targetType)
        {
        }

        class CustomMixinMemberData : MixinMemberData
        {
            private readonly string _targetName;

            public CustomMixinMemberData(string targetName, IMixinBuilder source, Func<object> make = null) : base(source, make)
            {
                _targetName = targetName;
            }

            public override void SetValue(object owner, object value)
            {
                TypeData.GetTypeData(owner).GetMember(_targetName).SetValue(owner, value);
            }

            public override object GetValue(object owner)
            {
                return TypeData.GetTypeData(owner).GetMember(_targetName).GetValue(owner);
            }
        }
        
        public MixinMemberData ToDynamicMember(ITypeData targetType)
        {

            return new CustomMixinMemberData(MemberTargetName, this, () => 0.0)
            {
                Name = MemberTargetName + "_Mirror",
                TypeDescriptor = TypeData.FromType(typeof(double)),
                Writable = true,
                Readable = true,
                DeclaringType = targetType,
                Attributes = [],
                DefaultValue = 0.0
            };
        }
    }

This gives this behavior: