Неявное приведение массива в C # - PullRequest
4 голосов
/ 15 июня 2010

У меня есть следующие классы с определенным неявным оператором приведения:

class A
{
    ...
}
class B
{
    private A m_a;

    public B(A a)
    {
        this.m_a = a;
    }

    public static implicit operator B(A a)
    {
        return new B(a);
    }
}

Теперь я могу неявно привести A к B.

Но почему я не могу неявно привести A [] к B []?

static void Main(string[] args)
{
    // compiles
    A a = new A();
    B b = a;

    // doesn't compile
    A[] arrA = new A[] {new A(), new A()};
    B[] arrB = arrA;
}

Спасибо, Малки.

Ответы [ 4 ]

8 голосов
/ 15 июня 2010

Как упоминал Мехрдад Афшари, вам не везет, если вы делаете это неявно.Вы должны будете получить явное, и это будет включать в себя копию массива.К счастью, вы, вероятно, можете сделать это с помощью однострочного:1007 *

5 голосов
/ 15 июня 2010

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

1 голос
/ 07 октября 2012

ConvertAll

Просто для ясности, вот как вы используете ConvertAll.

В случае, если class B имеет член class A с именем m_a, сделайте следующее:

B[] arrB;
A[] arrA = Array.ConvertAll(arrB, b => b.m_a);

.

Если class B имеет некоторые данные-члены, которыми вам нужно манипулировать, прежде чем вы сможете вернуть объект типа A (например, преобразование набора числовых значений в описание строки), выполните следующее:

class B
{        
    public static A makeAfromB(B b)
    {
        // Do something with B data...

        A a = new A("data made from B data")
        return a;
    }

    // rest of class B implementation ...
}

// somewhere else in your code...
A[] arrA = Array.ConvertAll(arrB, new Converter<B, A>(B.makeAfromB));

.

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

A[] arrA = Array.ConvertAll(arrB, new Converter<B, A>(
    delegate(B b)
    {
        // Do something with B data, though object b is const

        A a = new A("data made from B data")
        return a;
    }));

.

Было бы неплохо, если бы ConvertAll мог использовать неявный оператор для преобразования, но я не понял, как это сделать.

.

Cast

@ Randolpho @ узкое место

Для случаев, когда вы просто хотите выполнить итерацию и предпочитаете не делать копию, использование преобразования имеет смысл. Однако я не смог заставить его работать.

У меня есть класс InkPoint, в котором есть объект Point и некоторые другие члены. Я хочу вызвать функцию DrawLines(Pen, Point[]). Тем не менее, у меня есть массив InkPoint[].

Вот мой класс и код, который я сейчас должен использовать, что делает копию:

public class InkPoint
{
    public InkPoint(int x, int y)
    {
        point = new Point(x, y);
    }

    public Point point { get; set; }

    public static implicit operator Point(InkPoint p)
    {
        return p.point;
    }
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
    InkPoint[] inkPoints = { new InkPoint(1,2), new InkPoint(3,4) };
    Point[] points = Array.ConvertAll(inkPoints, x => x.point);

    Pen pen = new Pen(Color.Black, 1);
    e.Graphics.DrawLines(pen, points);
}

.

Я бы скорее назвал это, но он не скомпилируется, ссылаясь на недопустимые аргументы:

e.Graphics.DrawLines(pen, inkPoints.Cast<Point>()); // Compile err: invalid args

.

Я также пытался выполнить итерации по приведениям, но выдает исключение, ссылаясь на то, что приведение не допустимо

foreach (Point p in inkPoints.Cast<Point>()) { } // Exception: cast not valid

.

Я не понимаю, почему specified cast is not valid, поскольку я определил неявный оператор. Я могу сделать следующее очень хорошо:

InkPoint ip = new InkPoint(10, 20);
Point p1 = ip; // implicit conversion
Point p2 = (Point)ip; // cast

.

Для меня ситуация на самом деле немного сложнее. У меня на самом деле есть список InkPoints, List<InkPoint>, но функция DrawLines принимает только массивы. Итак, мой код выглядит так:

List<InkPoint> inkPoints = new List<InkPoint>();
inkPoints.Add(new InkPoint(5, 10));
inkPoints.Add(new InkPoint(10, 15));
Point[] points = inkPoints.ConvertAll<Point>(x => x.point).ToArray();

Я могу немного изменить это так:

Point[] points = Array.ConvertAll(inkPoints.ToArray(), x => x.point);

.

Так что я думаю, что на самом деле здесь происходит две копии. Это раздражает, так как все, что я хочу сделать, это нарисовать линии. Не кажется необоснованным, что функция DrawLines должна иметь возможность перебирать некоторый массив / список, содержащий ссылки на объекты, которые могут быть неявно преобразованы в Point объекты.

0 голосов
/ 15 июня 2010

Представьте себе на минуту, что массивы используют тот же синтаксис, что и другие коллекции в .Net, и вы пытаетесь сравнить это Array<A> с Array<B>.Вы бы не сравнили List<A> с List<B>.Ну, это по сути то, что вы пытаетесь.

Я бы порекомендовал использовать простой метод расширения, чтобы получить желаемый результат, вам нужно немного изменить синтаксис, чтобы сказать 'B [] arrB = arrA.ToBArray (); `

static class ArrayCast {
    public static B[] ToBArray(this A[] source) {
        var result = new B[source.Length];
        for (int i = 0;i < source.Length;i++)
            result[i] = source[i];
        return result;
    }
}
...