Home > database >  How to enforce implementation of nested class properties?
How to enforce implementation of nested class properties?

Time:02-13

It's been years since I've had to ask a question on StackOverflow. Yet I seem to lack basic understanding, please help me out.

I'm trying to implement my own multi-language feature in Blazor WASM. Nothing fancy but I would like to make sure I don't forget to implement properties in a specific language. In TypeScript this would work with typing or interfacing objects. In C# the equivalent of such an object would be a class. I tried using interfaces and abstract classes in C# but I'm not getting the desired result.

The desired result would be for my code editor to hint when I forgot to implement a property. How should I proceed?

I could just put all properties directly into the Translation class to enforce their implementation but than I'll have one very long list of properties, I'd prefer to chop them up a bit.

    public class NavMenuProps
    {
        public string FirstMenuHeader { get; set; } = "Dogs";
        public string SecondMenuHeader { get; set; } = "Facilities";
        public string ThirdMenuHeader { get; set; } = "Profile";
    }
    
    public abstract class Translation
    {
        public abstract NavMenuProps NavMenu();
    }
    
    public class English : Translation
    {
        public override NavMenuProps NavMenu()
        {
            return new NavMenuProps();
        }
    }
        
    public class Dutch : Translation
    {
        public override NavMenuProps NavMenu()
        {
            return new NavMenuProps
            {
                FirstMenuHeader = "Honden",
                SecondMenuHeader = ""
            };
        }
    }

    public class French : Translation
    {
        public override NavMenuProps NavMenu()
        {
            throw new System.NotImplementedException();
        }
    }

In TypeScript it would work like this:


type TNavMenuProps = {
    firstMenuHeader: string;
    secondMenuHeader: string;
    thirdMenuHeader: string;
    ...
}

type TLoginPageProps = {
    firstLabel: string;
    secondLabel: string;
    ...
}

type TTranslation = {
    navMenu: TNavMenuProps;
    loginPage: TLoginPageProps;
    ...
}

const englishTranslations: TTranslation = {
    navMenu: {
        firstMenuHeader: "first";
        secondMenuHeader: "second";
        // Would complain I forgot "thirdMenuHeader".
    },
    loginPage: {
        firstLabel: "1st label";
        secondLabel: "2nd label";
    },
    ...
}

--- SOLVED ---

The solution is not that much better than having just one giant interface but it does what I need.

Solution inspired by the accepted answer:


    public interface INavMenuProps
    {
        public string FirstMenuHeader { get; set; }
        public string SecondMenuHeader { get; set; }
        public string ThirdMenuHeader { get; set; }
    }

    public interface ILoginPageProps
    {
        public string FirstLabel { get; set; }
        public string SecondLabel { get; set; }
    }
    
    public interface Translation : INavMenuProps, ILoginPageProps
    {
    }
    
    public class English : Translation
    {
        public string FirstMenuHeader { get; set; } = "Dogs";
        public string SecondMenuHeader { get; set; } = "Facilities";
        public string ThirdMenuHeader { get; set; } = "Profile";

        public string FirstLabel { get; set; } = "1st label";
        public string SecondLabel { get; set; } = "2nd label";
    }
        
    public class Dutch : Translation
    {
        
    }

    public class French : Translation
    {
        
    }

CodePudding user response:

If I understood your requirements, here is the solution

Declare NavMenu properties as interface

public interface NavMenuProps
{
    public string FirstMenuHeader { get; set; }
    public string SecondMenuHeader { get; set; }
    public string ThirdMenuHeader { get; set; }
}

Declare Translation class as abstract with abstract properties

public abstract class Translation : NavMenuProps
{
    public abstract string FirstMenuHeader { get; set; }
    public abstract string SecondMenuHeader { get; set; }
    public abstract string ThirdMenuHeader { get; set; }
}

Now you can implemented each language translation, if you do not implement NavMenuProps properties in any language class, code editor will complain like "Does not implement inherited abstract member"

public class English : Translation
{
    public override string FirstMenuHeader { get; set; }
    public override string SecondMenuHeader { get; set; }
    public override string ThirdMenuHeader { get; set; }
}

public class Dutch : Translation
{
    public override string FirstMenuHeader { get; set; }
    public override string SecondMenuHeader { get; set; }
    public override string ThirdMenuHeader { get; set; }
}

public class French : Translation
{
    public override string FirstMenuHeader { get; set; }
    public override string SecondMenuHeader { get; set; }
    public override string ThirdMenuHeader { get; set; }
}

CodePudding user response:

If I understand your requirements correctly, this could be a way to go: Make NavMenuProps abstract, and implement NavMenu() inside Translation. Now, rather than implementing NavMenu() in all of the classes that inherit Translation, you rather need to implement all properties in NavMenuProps in all those classes.

public abstract class NavMenuProps
{
    public abstract string FirstMenuHeader { get; }
    public abstract string SecondMenuHeader { get; }
    public abstract string ThirdMenuHeader { get; }
}

public abstract class Translation : NavMenuProps
{
    public Translation NavMenu() => this;
}

public class English : Translation
{
    public override string FirstMenuHeader => "First";
    public override string SecondMenuHeader => "Second";
    public override string ThirdMenuHeader => "Third";
}

Now you can call

var englishMenu = new English().NavMenu();

If there is any need to implement NavMenu() in any other way for some classes, you could make it virtual and override it where needed:

public abstract class Translation : NavMenuProps
{
    public virtual Translation NavMenu() => this;
}

public class English : Translation
{
    //...

    public override Translation NavMenu()
    {
        //return base.NavMenu();
    }
}

  • Related