Matriz de matriz implícita em C#
-
27-09-2019 - |
Pergunta
Eu tenho as seguintes aulas com um operador de elenco implícito definido:
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);
}
}
Agora, posso lançar implicitamente a B.
Mas por que não posso implicitamente lançar um [] para 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;
}
Obrigado, Malki.
Solução
Como Mehrdad Afshari mencionou, você está sem sorte fazendo isso implicitamente. Você terá que ficar explícito e isso envolverá uma cópia da matriz. Felizmente, você provavelmente pode fazer isso com uma linha:
arrB = arrA.Cast<B>().ToArray();
Embora se você quiser apenas iterar arrB
em um foreach
declaração, você pode evitar a cópia omitindo ToArray()
Outras dicas
A covariância da matriz funciona apenas para tipos de referência e na hierarquia da herança (observe que não é uma conversão que muda a representação: apenas um conjunto de ponteiros com tamanho idêntico interpretados de maneira diferente.) Não funcionará para tipos de valor e conversões definidas pelo usuário.
Converter tudo
Só para ser explícito, eis como você usa o Convertall.
No caso onde class B
tem um membro de class A
chamado m_a
, fazem isto:
B[] arrB;
A[] arrA = Array.ConvertAll(arrB, b => b.m_a);
.
Se class B
Tem alguns dados de membros que você precisa manipular antes de retornar um objeto do tipo A (como transformar vários valores numéricos em uma descrição da string), faça isso:
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));
.
Você também pode usar funções Lambda:
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;
}));
.
Seria bom se a Convertall pudesse usar o operador implícito para fazer a conversão, mas não descobri como fazer isso.
.
Elenco
@Randolpho @bottlenecked
Para os casos em que você simplesmente deseja iterar e prefere não fazer uma cópia, o uso do elenco faz sentido. No entanto, não consegui fazê -lo funcionar.
eu tenho um InkPoint
classe que tem um Point
objeto e alguns outros membros. Eu quero ligar para o DrawLines(Pen, Point[])
função. No entanto, eu tenho uma variedade de InkPoint[]
.
Aqui está minha aula e o código que atualmente preciso usar, o que faz uma cópia:
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);
}
.
Prefiro chamar isso, mas não vai compilar, citando argumentos inválidos:
e.Graphics.DrawLines(pen, inkPoints.Cast<Point>()); // Compile err: invalid args
.
Eu também tentei iterar um elenco, mas ele joga uma exceção, citando o elenco não é válido
foreach (Point p in inkPoints.Cast<Point>()) { } // Exception: cast not valid
.
Eu não entendo por que o specified cast is not valid
Desde que eu defini um operador implícito. Eu sou capaz de fazer o seguinte bem:
InkPoint ip = new InkPoint(10, 20);
Point p1 = ip; // implicit conversion
Point p2 = (Point)ip; // cast
.
Para mim, a situação é realmente um pouco mais complicada do que isso. Na verdade, eu tenho uma lista de tintas, List<InkPoint>
, mas o DrawLines
Função aceita apenas matrizes. Então, meu código se parece com o seguinte:
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();
Eu posso reorganizá -lo um pouco para isso:
Point[] points = Array.ConvertAll(inkPoints.ToArray(), x => x.point);
.
Então eu acho que há realmente duas cópias acontecendo aqui. Isso é irritante, pois tudo o que quero fazer é desenhar as linhas. Não parece irracional que o DrawLines
A função deve ser capaz de iterar sobre alguma matriz/lista que contém referências a objetos que podem ser implicitamente convertidos para Point
objetos.
Imagine por um momento se as matrizes usavam a mesma sintaxe que outras coleções no .NET, e o que você está tentando comparar é um Array<A>
com um Array<B>
. Você não compararia um List<A>
para um List<B>
. Bem, isso é essencialmente o que você está tentando.
Eu recomendo usar um método de extensão simples para obter o resultado que você deseja, você precisará alterar sua sintaxe um pouco para dizer '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;
}
}