0

The issue I've encountered here, is that I am currently trying to make an object that has a method as a property. I was able to get it to work on public static void methods, but not methods that return something I want.

To explain what I am trying to do, I am trying to write a small multiple choice game that randomizes the question and the choices you have. It has a list of students with properties such as height, weight, and score, and questions that would ask questions like who is the tallest, or who has the lowest score among them.

I did manage to write a working one and managed to refactor quite a bit, but I wanted to improve upon my code so I decided to try making a method into a property. (my original had so many ifs and uses unique id numbers to determine how to sort a list of choices)

Below is the main code:

static void Main(string[] args)
        {     

            Shuffler shuffler = new Shuffler();
            shuffler.Shuffle(QuestionList.listQuestions);

            Question tester = QuestionList.listQuestions[0];

            Console.WriteLine(tester.text);
            tester2.method();

            Console.ReadLine();
        }
    }

So what I wanted to do, is grab the list of questions I created in another class, shuffle it, and then have it execute the first one.

The code below is the one that I got it to work:

public class Question
    {
        public string text { get; set; }
        public Action method { get; set; }

        public Question()
        {

        }

        public Question(string textInput, Action methodInput)
        {
            text = textInput;
            method = methodInput;
        }
    }

    public class QuestionList
    {
        public static List<Question> listQuestions = new List<Question>();

        static QuestionList()
        {
            PopulateQuestionList();
        }

        private static void PopulateQuestionList()
        {
            listQuestions.Add(new Question("Who has the latest birthday?", Question1));
            listQuestions.Add(new Question("Who has the earliest birthday?", Question2));
            listQuestions.Add(new Question("Who is the tallest?", Question3));
            listQuestions.Add(new Question("Who is the shortest", Question4));
        }

        public static void Question1()
        {
            Console.WriteLine("Question 1");
        }

        public static void Question2()
        {
            Console.WriteLine("Question 2");
        }

        public static void Question3()
        {
            Console.WriteLine("Question 3");
        }

        public static void Question4()
        {
            Console.WriteLine("Question 4");
        }

So when I run the code, it would give me "Who has the latest birthday?" and "Question 1". However, this is the point where I was stuck. The below is the method I changed it to:

public static List<Student> Question1(List<Student> testing)
{
    testing.Sort((x, y) => y.birthday - x.birthday);
    return testing;
}

The code itself, at least when I tried to use it in my working quiz, works. (I take the list of 4 choices, randomize it in descending order, and if the user answer matches the first one, it is correct.) However, in this case, it had a error stating that the Question constructor was not satisfied.

What I tried to do, was to then see if a simpler method would work, one that does not require me to use a custom class like below, and it still returned the same issue.

        public static string Question1(string test)
        {
            test = "determine";
            return test;
        }

When I searched online, I read it was because an Action variable delegates to a function without any arguments, so that is why a void method worked but not one that requires something to be returned. So my thinking then, of course, was that there might be a variable that allows argument inputs. However, it seems I was wrong in my approach.

So I wanted to ask, is there anything I could do? I am quite new to coding, as in a month only, and some solutions I found online are concepts and posts I do not even know how to approach.

Thank you for reading. If there are further clarification or details required, I would gladly provide them.

5
  • Yes, Action takes no argument. There's Action<T> for methods that take a single argument. Commented Aug 1, 2023 at 2:03
  • And if you want to return something there is Func<T,TResult> Commented Aug 1, 2023 at 3:20
  • Func<T,TResult> worked. Thank you. However, I am wondering, so far my questions are rather simple comparisons, but if there was a need for it, are there variables that accept more than one argument? I also would like to add, these sound like very basic fixes, yet they eluded me so easily. The video lessons I watched then and what I'm doing felt like dipping my finger on the surface. Are there any good ways to improve?
    – kenLeeDep
    Commented Aug 1, 2023 at 4:07
  • I think using properties in this context leads to a bad design. This is a general shortcoming of oop and not of the author. Using a dictionary to keep the data for use in the quiz will result in a much more straightforward design, instead of using a Student class. Commented Aug 1, 2023 at 12:17
  • I'll look into dictionaries, thank you.
    – kenLeeDep
    Commented Aug 1, 2023 at 20:04

1 Answer 1

0

If you want a delegate to take an argument you can use Action<T>, and there are variants that take multiple arguments, Action<T1, T2> and so on. If you also want to return a value you can use Func<T1, TResult>, where the last generic type argument is always the return type. Also with variants that take more arguments, So the signature should be Func<List<Student>, List<Student>> for your list example. But you can also declare your own:

public delegate List<Student> MyQuestionDelegate(List<Student> testing);

This may improve readability if the generic variants become to long, but in my experience Action/Func is more common today, so new developers may not be familiar with custom delegates.

But a more concerning problem is what API you intend to have for your question. If your Question method signature is List<Student> Question(List<Student> testing) you will limit yourself to questions about students. Maybe that is fine? I do not know your problem domain. But for something like multiple choices questions I would have expected something more abstract, like:

public interface IQuestion{
    public string Question {get;}
    public IReadOnlyList<string> Choices {get;}
    public int CorrectChoiceIndex {get;}
}
1
  • Thank you for the answer. It helped clear quite a bit the problem I was facing. Thanks.
    – kenLeeDep
    Commented Aug 1, 2023 at 20:05

Not the answer you're looking for? Browse other questions tagged or ask your own question.