Home > other >  How do I expose a record property using an extension method and making the property private
How do I expose a record property using an extension method and making the property private

Time:10-13

I'm trying to make it to where I can't access the record properties based on their name but rather though an extension method.

For instance, I want to be able to do this Axe.GetById(1378).GetName()
But not be able to do Axe.GetById(1378).Name

I tried declaring a private field inside the record, but then I can't access it in my extension method at all in order to return it.

public sealed record AxeEnum(string Name, int LevelReq, int ItemId)
{
    private int privateField;
}
static class Axe
{
    static Dictionary<int, AxeEnum> AXES = new Dictionary<int, AxeEnum>();
    static AxeEnum STEEL = new AxeEnum("Steel", 41, 1378);


    static Axe()
    {
        AXES.Add(GetId(STEEL), STEEL);
    }

    public static AxeEnum GetById(int id)
    {
        AXES.TryGetValue(id, out var axe);
        return axe;
    }

    public static int GetLvlReq(this AxeEnum axe) => axe.LevelReq;
    public static int GetId(this AxeEnum axe) => axe.ItemId;
    public static string GetName(this AxeEnum axe) => axe.Name;
}

CodePudding user response:

where I can't access the record properties based on their name

If you don't want the shape that records present: don't use records; just create your own class with whatever members and accessibility rules you want.


I tried declaring a private field inside the record, but then I can't access it in my extension method at all in order to return it.

Extension methods must still respect accessibility rules; at the moment the field is private, which is inaccessible except by code in that type, or nested types, and extension methods must be on top-level (non-nested) types. We can't make use of protected, since extension methods must be on static types (which can't inherit anything), plus the record here is sealed. So: that leaves internal:

public sealed record AxeEnum(string Name, int LevelReq, int ItemId)
{
    internal int privateField;
}

Personally, though: I'd probably just make this a public instance property or method on the record itself.

CodePudding user response:

It's sort-of possible to do this if you make AxeEnum a class instead of a record and declare it nested inside the Axe class.

You also have to make a private nested interface for the properties you want to access from within the class.

It would look something like this:

static class Axe
{
    // Private interface for the properties that this class needs to access.
    interface IAxeEnumPrivates
    {
        string Name     { get; }
        int    LevelReq { get; }
        int    ItemId   { get; }
    }

    // Nested class with explicit implementation of the properties.
    public sealed class AxeEnum : IAxeEnumPrivates
    {
        public AxeEnum(string name, int levelReq, int itemId)
        {
            _name     = name;
            _levelReq = levelReq;
            _itemId   = itemId;
        }

        string IAxeEnumPrivates.Name  => _name;
        int IAxeEnumPrivates.LevelReq => _levelReq;
        int IAxeEnumPrivates.ItemId   => _itemId;

        readonly string _name;
        readonly int    _levelReq;
        readonly int    _itemId;
    }

    static Dictionary<int, AxeEnum> AXES  = new Dictionary<int, AxeEnum>();
    static AxeEnum STEEL = new AxeEnum("Steel", 41, 1378);

    static Axe()
    {
        AXES.Add(GetId(STEEL), STEEL);
    }

    public static AxeEnum? GetById(int id)
    {
        AXES.TryGetValue(id, out var axe);
        return axe;
    }

    public static void DoSomethingWithAxe(AxeEnum axe)
    {
        // Need to assign the concrete class to a variable of the interface type in order
        // to access its properties.

        IAxeEnumPrivates iAxe = axe;

        // Now you can do things with the properties:

        Console.WriteLine(iAxe.Name);
        Console.WriteLine(iAxe.LevelReq);
        Console.WriteLine(iAxe.ItemId);

        // But you can't access the properties via the concrete class:

        // Console.WriteLine(axe.Name); // Won't compile!
    }

    // We must implement these by casting the concrete AxeEnum class
    // to the private interface, so that we can access the properties.
    // This is completely safe because AxeEnum implements the interface.

    public static int GetLvlReq(this AxeEnum axe) => ((IAxeEnumPrivates)axe).LevelReq;
    public static int GetId(this AxeEnum axe) => ((IAxeEnumPrivates)axe).ItemId;
    public static string GetName(this AxeEnum axe) => ((IAxeEnumPrivates)axe).Name;
}

However this looks like a fiddly solution to an X-Y problem...

Also see this related answer from Eric Lippert

  • Related