简体   繁体   中英

C#: Internal and public interfaces, auto-implemented properties, but with structs…

I know there's a pattern involving partial classes to implement a class that has settable properties to the assembly, but is read-only from the outside, something like

// This is just the non-working example to get the idea of what I want it to do across

public interface IMyReadable
{
    int Property1 { get; }
    string Property2 { get; }
}

internal interface IMyEditable : IMyReadable
{
    int Property1 { get; set; }
    string Property2 { get; set;}
}

public class Implementation : IMyEditable
{
    public int Property1 { get; internal set; }
    public string Property2 { get; internal set;}
}

since you can't define 'internal set' in an interface. It can be implemented for classes as eg https://stackoverflow.com/a/42082500/6155053

…but what if I want to do this for structs?

I know, structs should be completely read-only usually, but it would be nice to have multiple different struct implementations implementing the interface, and then being able to instance them first and init them property by property afterwards from inside the assembly, eg in a factory class, without having to write the setting for each of the interface's properties out for each different implementation (via their c'tors or something).

That would allow me to do something like

// non-sense, but explanatory code snippet
[…]
var structs = new HashSet<IMyEditable>();
structs.Add(new MyEditableImplementation1());
structs.Add(new MyEditableImplementation2());
structs.Add(new MyEditableImplementation3());

int i = 0;
foreach(IMyEditable s in structs)
{
    s.Property1 = ++i;
    s.Property2 = i.ToString();
}

// ...and still be able to set custom properties only some of the implementations
// have afterwards, etc., and when you then return, for example do:

return structs.Cast<IMyReadable>();

Would save writing a lot of boilerplate for initialization as the number of implementations of the interface increases.

However, since structs are always sealed and can't inherit from a "base struct"… how would I do this without losing the advantage of auto-implemented properties (which I would if I'd do it with proxy properties with shared private fields inside every struct—that, again, means a lot of boilerplate code, only in another place).

Is this possible? If so, how?

Edit: Added 2nd sample to explain desired usage

How about this?

在此处输入图片说明

IReader.cs (ClassLibrary1)

namespace ClassLibrary1
{
    public interface IReader
    {
        int Property1 { get; }
        string Property2 { get; }
    }
}

IWriter.cs (ClassLibrary1)

namespace ClassLibrary1
{
    internal interface IWriter : IReader
    {
        new int Property1 { get; set; }
        new string Property2 { get; set; }
    }
}

SomeObject.cs (ClassLibrary1)

namespace ClassLibrary1
{
    internal class SomeObject : IWriter
    {
        public int Property1 { get; set; }
        public string Property2 { get; set; }
    }
}

Worker.cs (ClassLibrary1)

namespace ClassLibrary1
{
    using System.Collections.Generic;
    using System.Linq;

    public static class Worker
    {
        // NOTE: Thanks to internal on the IWriter, you can't return IWriter here.
        public static IEnumerable<IReader> DoSomething()
        {
            var structs = new HashSet<IWriter>();
            structs.Add(new SomeObject());
            structs.Add(new SomeObject());
            structs.Add(new SomeObject());

            int i = 0;
            foreach (IWriter s in structs)
            {
                s.Property1 = ++i;
                s.Property2 = i.ToString();
            }

            return structs.Cast<IReader>();
        }
    }
}

Program.cs (ConsoleApplication3)

namespace ConsoleApplication3
{
    using System.Collections.Generic;
    using System.Linq;
    using ClassLibrary1;

    public class Program
    {
        private static void Main(string[] args)
        {
            // NOTE: You can't cast the structs to IWriter here.
            var structs = Worker.DoSomething();

            // This will not compile:
            // (structs.FirstOrDefault() as IWriter).Property1 = 10
        }
    }
}

Note that:

  • IWriter is internal , which means you can't pass it/use it outside of ClassLibrary1. Perfect, only your 'internals' can work on it and to the outside world you can expose IReader only, which is what we do.
  • In the Console App, you can't cast the object to IWriter . Which is good.

Interestingly, the 'Immediate window' allows me to cast the struct to IWriter , but I think it uses reflection and this is not allowed in compile time, so you are good!

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