Универсальный класс, который может принимать только определенные типы - PullRequest
0 голосов
/ 27 июня 2018

Предположим, я хочу создать универсальный класс, который может принимать только int и double в качестве типов.

public class A<T> where T: int, double
{
    public T property{get;set;}
}

Например:

A<int> i = new A<int>();
i.property = 10;

A<double> d = new A<double>();
d.property = 0.01;

но это не работает.

Как я могу это сделать?

Есть ли другой способ удовлетворить мои конкретные требования?

Ответы [ 6 ]

0 голосов
/ 06 июля 2018

Если ваша единственная цель состоит в том, чтобы предотвратить использование A с чем-либо, кроме int или deouble, это будет работать. Комментарий XML может, по крайней мере, сказать потенциальным разработчикам, что они ограничены.

/// <summary>
/// T can only be an int or double or will throw an exception on construction.
/// </summary>
/// <typeparam name="T">Must be int or double.</typeparam>
public class A<T>
{
    public A()
    {
        if (!(property is int || property is double))
            throw new Exception("A can only work with int and double");
    }
    public T property  { get; set; }
}
0 голосов
/ 03 июля 2018

Насколько я понимаю, дженерики C # у вас есть несколько "режимов", в которых вы можете настроить дженерики:

  • Без указания общего параметра, то есть открытого для каждого типа, примитива и т. Д.
  • С указанием фильтра, такого как where T : class или where T : BaseClass или даже where T : ISomeInterface, в котором вы указываете, что передаваемый тип должен строго являться классом, классом, наследуемым от BaseClass, или типом, который реализует ISomeInterface соответственно .

Насколько мне известно, нет способа ограничить, какие структуры или примитивы вы можете использовать. Вероятно, лучше всего было бы создать отдельные реализации, используя примитивные типы, которые вы хотите использовать. В качестве альтернативы вы можете создать конструктор, который проверяет, соответствует ли переданный тип допустимому типу, и если нет, выдает исключение.

public class MyGenericClass<T> 
{
    public MyGenericClass()
    {
        if(typeof(T) != typeof(int)) throw new Exception("You have passed an invalid type");
    }
}

Надеюсь, это поможет.

0 голосов
/ 03 июля 2018

Вы можете добиться этого, используя обходной путь со статическими методами и сделав конструктор внутренним. Однако это работает, только если A <> находится в другой библиотеке. Если все находится в одной библиотеке, это решение не будет работать.

public class Program
{
    public static void Main()
    {
        var a = A.CreateDecimal();
        a.property = 7;
    }
}

public class A<T>
{
    public T property;
    internal A()
    {
    }
}

public static class A
{
    public static A<decimal> CreateDecimal() => new A<decimal>();
    public static A<int> CreateInt() => new A<int>();
}
0 голосов
/ 03 июля 2018

Вы можете добавить конструктор private к классу A<T> и объявить два соответствующих класса: A_int и A_double внутри , чтобы иметь возможность наследовать их от A<T> - они становятся "дружелюбный" до A<T>. Но для классов, объявленных вне этой области (Test class), это невозможно, как и для прямого создания из-за конструктора private, который мы должны вызывать, но не можем. Таким образом, на практике у вас будет только два варианта A<T>, дополненных время компиляции уведомления о недопустимых использованиях:

public class A<T> where T : struct
{
    //constructor surely can have arguments
    private A()
    {
    }

    public T property { get; set; }
    //and other common stuff

    //each class declaration below we can treat like "where" constraint
    public class A_int : A<int> { }
    public class A_double : A<double> { }
}

//compile time error:
//'A<bool>.A()' is inaccessible due to its protected level
public class Test : A<bool>
{
}

Использование:

using static NameSpaceName.A<int>;
//you should not care about <int> - it is only needed for compiler 
//and won't have any influence

var intVar = new A_int();
var doubleVar = new A_double(); 

//compile time error:
//'A<decimal>.A()' is inaccessible due to its protected level
var decimalVar = new A<decimal>(); 
0 голосов
/ 02 июля 2018

Вы можете написать свой собственный Wrapper и использовать для них базовый интерфейс. Например:

public class Integer : IMyNumber
{
    public int Value { get; set; }


    public Integer() { }
    public Integer( int value ) { Value = value; }


    // Custom cast from "int":
    public static implicit operator Integer( Int32 x ) { return new Integer( x ); }

    // Custom cast to "int":
    public static implicit operator Int32( Integer x ) { return x.Value; }


    public override string ToString()
    {
        return string.Format( "Integer({0})", Value );
    }
}


public interface IMyNumber
{
    // nothing needed
}

Тогда вы можете написать свой общий класс:

public class A<T> where T : IMyNumber
{
    public T property;
}

И вы можете использовать его:

A<Integer> i = new A<Integer>();
i.property.Value = 10;
0 голосов
/ 27 июня 2018

Нет такого ограничения в C #. Но для типа значения вы можете использовать struct в качестве общего ограничения. Это позволит только типы значений, не допускающие значения NULL.

public class A<T> where T : struct
{
    public T property;
}

Вы можете добавить проверку типа во время выполнения в конструкторе:

public class A<T> where T : struct
{
    public T property;
    public A()
    {
        if(typeof(T) != typeof(int) || typeof(T) != typeof(double))
        {
            throw new InvalidConstraintException("Only int or double is supported");
        }
    }
}
...