Обнуляемый <T>как параметр - PullRequest
0 голосов
/ 11 апреля 2019

Я пытался возиться с общими типами и абстракциями для личного проекта библиотеки, но я столкнулся с проблемой. Я нашел этот пост , это было похоже на то, что я хотел сделать, но я хотел сделать еще один шаг вперед. потому что я хотел ограничить мою функцию универсальным параметром только несколькими типами что-то вроде:

public static T Read<T>(T? min, T? max) where T: int, float, double, anything i want
{
}

Я знаю, что это невозможно, но я пытаюсь найти обходные пути для достижения чего-то похожего

Я пытался установить для использования: T? но я получаю сообщение о том, что T не может быть обнуляемым для использования в качестве параметра. Как вы можете видеть из:

where F : ConsoleReadType<T>

Я пытаюсь разрешить запуск только унаследованных классов.

public abstract class ConsoleReadType<T>
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);
    public virtual F ReadUntilCorrect<F>(Func<F> FunctionToRun, string message = "") /*where F : ConsoleReadType<T>*/
    {
        while (true)
        {
            try
            {
                return FunctionToRun();
            }
            catch (ConsoleInputException)
            {
                if (!string.IsNullOrEmpty(message))
                    ConsoleWrite.Error(message);
            }
        }
    }
}

public class ConsoleReadDouble : ConsoleReadType<double>
{
    public override double Read()
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException();
        }
        return ret;
    }
    public override double Read(double? min, double? max)
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException("invalid input format");
        }
        if (min.HasValue && ret < min || max.HasValue && ret > max)
        {
            throw new ConsoleInputException("input value should be between: " + min + " and " + max);
        }
        return ret;
    }
}

Итак, основные вопросы:
1. Есть ли способ установить обнуляемые переменные T абстрактно или есть лучший способ добиться того, что я пытаюсь сделать?
2. Могу ли я разрешить только определенные функции с оператором where?
3. Есть ли способ сделать эти классы статичными в конце, чтобы их можно было использовать в качестве помощника, не создавая их экземпляры?
4. Меня также интересует любой совет, который вы могли бы дать мне по поводу моего кода

Большое спасибо.

Ответы [ 2 ]

3 голосов
/ 11 апреля 2019

Вы можете использовать только это:

// add where T: struct so that only structs (int, double, etc) can be used
// allows you to use T? 
public abstract class ConsoleReadType<T> where T: struct
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);
    public virtual T ReadUntilCorrect(Func<T> FunctionToRun, string message = "")
    {
        while (true)
        {
            try
            {
                return FunctionToRun();
            }
            catch (ConsoleInputException)
            {
                if (!string.IsNullOrEmpty(message))
                    ConsoleWrite.Error(message);
            }
        }
    }
}

Есть ли способ сделать эти классы статичными в конце, чтобы их можно было использовать в качестве помощника, не создавая их экземпляры?

Не совсем, вы не можете наследовать от статического класса, поэтому вам придется удалить класс ConsoleReadType<T>. Вы можете, однако, использовать Фабричный подход:

public static class ConsoleReader
{
    public static ConsoleReadType<T> GetReader<T>()
    {
        if (typeof(T) == typeof(double))
        {
            return new ConsoleReadDouble();
        }
        // etc
    }
}

Меня также интересует любой совет, который вы могли бы дать мне по поводу моего кода

По-моему, вам вообще не нужен Read();, должно хватить Read(T? min, T? max);. Тогда ReadUntilCorrect не должен получать Func<T>, а вместо этого звонить Read. Вы можете сделать только с:

public abstract class ConsoleReadType<T> where T: struct
{
    public abstract T Read(T? min = null, T? max = null);
    public virtual T ReadUntilCorrect(T? min = null, T? max = null)
    {
        while (true)
        {
            try
            {
                return Read(min, max);
            }
            catch (ConsoleInputException ciex)
            {
                ConsoleWrite.Error(ciex.Message);
            }
        }
    }
}
1 голос
/ 11 апреля 2019

Вы можете использовать ограничение struct (where T: struct), чтобы ограничить универсальный тип значениями. Затем вы можете использовать Nullable<T> / T?.

public abstract class ConsoleReadType<T> where T: struct
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);
}

Уловка для реализации ReadUntilCorrect и возможности использования статических вызовов методов - это использование конкретного унаследованного класса в качестве параметра типа для абстрактного базового класса:

public abstract class ConsoleReadType<T, ConcreteReaderT> 
 where T: struct 
 where ConcreteReaderT: ConsoleReadType<T, ConcreteReaderT>, new()
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);

    public static T ReadUntilCorrect(string message = "") 
    {
        ConcreteReaderT reader = new ConcreteReaderT();
        while (true)
        {
            try
            {
                return reader.Read();
            }
            catch (ConsoleInputException)
            {
                if (!string.IsNullOrEmpty(message))
                    Console.Error.WriteLine(message);
            }
        }
    }
}

// Use the concrete class as its own type parameter so that
// the base class can use the concrete class without knowing it 
// beforehand
public class ConsoleReadDouble : ConsoleReadType<double, ConsoleReadDouble>
{
    public override double Read()
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException();
        }
        return ret;
    }
    public override double Read(double? min, double? max)
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException("invalid input format");
        }
        if (min.HasValue && ret < min || max.HasValue && ret > max)
        {
            throw new ConsoleInputException("input value should be between: " + min + " and " + max);
        }
        return ret;
    }
}

Затем вы можете использовать конкретные классы следующим образом:

class Program
{
    static void Main(string[] args)
    {
        double d = ConsoleReadDouble.ReadUntilCorrect("Please enter a valid number");

    }
}
...