Явное приведение к другому типу в C # - PullRequest
0 голосов
/ 20 февраля 2019

У меня есть следующий код для C ++, в шаблонном классе, который представляет точку.Я хотел бы перевести его на C #:

template <class T>
class Point
{
    public:
        T x;
        T y;
        T z;

    template<typename U> explicit Point(const Point<U> &p)
       : x((T)p.x), y((T)p.y), z((T)p.z)
    {
    }
}

Этот код позволяет явно привести точку данного типа в точку другого типа.Например, вы можете использовать что-то вроде этого (по общему признанию, я не уверен на 100% в синтаксисе здесь, но я понимаю концепцию):

Point<float> p;
Point<int> q = (Point<int>)p;

Как я могу включить эквивалент этого в C #?Пока у меня есть:

public class Point<T>
{
    public T X { get; set; }
    public T Y { get; set; }
    public T Z { get; set; }

    // Constructors exist that accept X, Y, and Z as parameters

    public static explicit operator Point<U>(Point<T> p)
    {

    }
}

Это дает ошибку, однако, говоря, что "U" не определено.Это имеет смысл ... но как / где я могу определить U?Мой подход неверен?

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

Ответы [ 5 ]

0 голосов
/ 21 февраля 2019

Вы не можете определить дополнительные ограничения универсального типа, но вы можете сделать что-то подобное, используя операторы и методы.

public class Point<T>
{
    public T X { get; set; }
    public T Y { get; set; }
    public T Z { get; set; }

    public static explicit operator Point<T>(Point<int> v)
    {
        return v.As<T>();
    }

    public static explicit operator Point<T>(Point<double> v)
    {
        return v.As<T>();
    }

    public static explicit operator Point<T>(Point<float> v)
    {
        return v.As<T>();
    }

    public Point<TU> As<TU>()
    {
        return new Point<TU>()
        {
            X = ConvertTo<TU>(X),
            Y = ConvertTo<TU>(Y),
            Z = ConvertTo<TU>(Z)
        };
    }

    private static TU ConvertTo<TU>(T t)
    {
        return (TU) Convert.ChangeType(t, typeof(TU));
    }
}

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

        Point<double> d = new Point<double>()
        {
            X = 10d, Y = 10d, Z = 10d
        };

        Point<int> i = (Point<int>) d;

        Point<float> f = (Point<float>) i;

        d = (Point<double>) f;
0 голосов
/ 21 февраля 2019

Вы не можете объявлять операторы с дополнительными аргументами универсального типа, но вы можете объявлять операторы для или из определенных универсальных типов, таких как Point<int>.C # также не позволит вам выполнять произвольные преобразования, приводя от или к T.

Наименее тяжелый вариант, поддерживающий минимальную безопасность типов, - это ограничение параметра T на IConvertible:

public class Point<T> where T : IConvertible
{
    // ...

    public static explicit operator Point<int>(Point<T> point)
    {
        // The IFormatProvider parameter has no effect on purely numeric conversions
        return new Point<int>(point.X.ToInt32(null), point.Y.ToInt32(null), point.Y.ToInt32(null));
    }    
}

Однако это не помешает пользователям объявлять бессмысленные, неподдерживаемые типы, такие как Point<DateTime>, которые затем будут выбрасываться во время выполнения при попытке преобразования.

0 голосов
/ 21 февраля 2019

Я думаю, что лучшее, что вы можете получить, это:

public class Point<T>
{
    public T X { get; set; }
    public T Y { get; set; }
    public T Z { get; set; }

    public Point<U> As<U>()
    {
        return new Point<U>()
        {
            X = Convert<U>(X),
            Y = Convert<U>(Y),
            Z = Convert<U>(Z)
        };
    }

    static U Convert<U>(T t) => (U)System.Convert.ChangeType(t, typeof(U));
}

Вы не можете определить универсальный оператор преобразования, поэтому вам нужно, чтобы он был явной функцией.Более того, простое приведение (U)t не будет работать, поэтому вам нужно Convert.ChangeType (которое будет работать, если ваши типы числовые).

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

var p1 = new Point<int> { X = 1, Y = 2, Z = 3 };
var p2 = p1.As<double>();

( работает как положено ).

0 голосов
/ 21 февраля 2019

Аналогично Ответ Кевина , но без dynamic - использовать двойное приведение:

public Point<U> To<U>()
{
    return new Point<U>((U)(object)X, (U)(object)Y, (U)(object)Z);
}

Оба наших ответа не улавливают никаких проблем во время компиляции.

0 голосов
/ 21 февраля 2019

Насколько я знаю, этот тип обобщенного приведения допускается только в C #, если между T и U.

существует какое-то наследственное отношение. Ближайшим эквивалентом будет определение обобщенногометод преобразования:

public Point<U> To<U>()
{
    dynamic p = this;

    return new Point<U>((U)p.X, (U)p.Y, (U)p.Z);
}

Вы не можете преобразовать напрямую T в U, так как компилятор не может знать, будет ли он безопасным.Я использую ключевое слово dynamic, чтобы обойти это ограничение.

...