C# dll中的类型转换异常

4

我有一个类,其中有一个字段被赋值为类型T的数组或者类型T的dll对象,该对象具有重载this [int i]以模拟数组(它使用非托管内存,并使用重载访问该内存)

    public T this[int i]
    {
        get
        {
            return ((T[])array)[i]; // can't use return array[i] directly
        }
        set { }
    }

当我使用dll对象时,它会抛出转换异常。 我原以为(T [])将触发对象的重载[]而不是强制转换为float [] ,但它总是强制转换为 float [] ,并且对象类型是 FloatArr ,其中具有 public float this [int i]

是否有一种方式可以将基元数组视为同时将其他自定义类型视为重载触发器的转换?

如果这不可能,如何返回类型 T ,而返回类型(如简单的浮点数数组或某些自定义类型,具有重载来返回浮点数)可以从中获取 float ? 我不想一个个添加float,int,byte,double,char等。 无论对象字段是C#数组还是自定义类型与重载索引器,我都需要类型 T 适用于所有情况?

实际上我需要什么:

    // developer supplies here with his/hers own array
    // or this library uses its own fast C++ wrapper with a [] overload
    public object array { get; set; }

    // returns float, int, byte
    public T this[int i]
    {
        get
        {
            return array[i]; // float[], int[], FloatArr[], ...
        }
        set { }
    }

编辑:如果这是一个不好的设计模式,请告诉我。我需要这个类将属性视为C#数组或带有[]重载的C++包装器C# dll对象。

编辑-2:这是我想要的用法:

MyArr<float> gpu = new MyArr<float>(1024);
gpu.array=new float[1024];
gpu[35]=40;

or


MyArr<float> gpu = new MyArr<float>(1024);
gpu.allocateUnManagedToArray(); // now array field is a custom object
gpu[35]=40;

编辑-3: 这里是我放入数组字段的自定义类:

public class FloatArr
{
    public float this[int i]
    {
        get
        {
            unsafe
            {
                float* p = (float*)hArr.ToPointer();
                return *(p + i);
            }

        }
        set {
            unsafe
            {
                float* p = (float*)hArr.ToPointer();
                *(p + i) = value;
            }
        }
    }
}

编辑-4: 以防我没有表达清楚:

  • 我有一个类,其中[]重载为数组
  • 但它也有一个字段,有时会给另一个重载[]的类!
  • 现在我只需要其他开发人员能够提供自己的C#数组,当他们不想要这个类自己的C++包装对象时。
  • 然后,在两种情况下,我都需要从同一字段(浮点数组或具有[]重载的C++包装器)进行索引。

当然,我可以逐一编写所有float int byte的条件,但这样会花费更长的时间。通用类很容易,应该很容易,但我在这里缺少了一些东西,我看不见。当我添加结构体数组时,代码将在未来增长得更多,因此我无助地需要通用类的帮助。


3
为什么不是 public T[] array { get; set; }?我不明白。 - Erik Philips
它也可以是未管理内存包装器(作为C++的分配器和访问者)。 - huseyin tugrul buyukisik
it 是什么?T 还是 T[]?由于 T 是泛型,我不知道为什么 T 不能是任何类型(托管或非托管)。 - Erik Philips
5
为什么 T[] 不能转换成一个数组呢?这正是你要告诉它做的事情。也许你实际上想转换为 IList<T>,然后使用提供的索引器?这里一份 [mcve](最小可复现代码示例)会非常有帮助... - Jon Skeet
2
你需要展示一个最小、完整和可验证的示例,其中使用了自定义包装类。你目前的示例并没有帮助我们解释为什么你的自定义包装器无法转换,因为你的示例中没有使用有效的自定义包装器,并且你也没有包含包装器的定义。你的新Edit2中也没有包含这些信息,我们需要看到allocateUnManagedToArray()的代码以及它所分配给array内部的类的完整定义。 - Scott Chamberlain
显示剩余8条评论
2个回答

1
您需要做的是不使用数组,而是使用IList<T>接口。然后您需要使您的自定义类实现该接口。
class FloatArr : IList<float>
{
    //(snip)
    // p and hArr's decliration and assignment
    //(snip)

    public float this[int index]
    {
        get
        {
            unsafe
            {
                float* p = (float*)hArr.ToPointer();
                return *(p + i);
            }

        }
        set
        {
            unsafe
            {
                float* p = (float*)hArr.ToPointer();
                *(p + i) = value;
            }
        }
    }

    IEnumerator<float> IEnumerable<float>.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    void ICollection<float>.Add(float item)
    {
        throw new NotImplementedException();
    }

    void ICollection<float>.Clear()
    {
        throw new NotImplementedException();
    }

    bool ICollection<float>.Contains(float item)
    {
        throw new NotImplementedException();
    }

    void ICollection<float>.CopyTo(float[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    bool ICollection<float>.Remove(float item)
    {
        throw new NotImplementedException();
    }

    int ICollection<float>.Count
    {
        get { throw new NotImplementedException(); }
    }

    bool ICollection<float>.IsReadOnly
    {
        get { throw new NotImplementedException(); }
    }

    int IList<float>.IndexOf(float item)
    {
        throw new NotImplementedException();
    }

    void IList<float>.Insert(int index, float item)
    {
        throw new NotImplementedException();
    }

    void IList<float>.RemoveAt(int index)
    {
        throw new NotImplementedException();
    }
}

如果您只需要读取数组而不需要写入,您可以使用 IReadOnlyList<T> 大大简化类。
class FloatArr : IReadOnlyList<float>
{
    //(snip)
    // p and hArr's decliration and assignment
    //(snip)

    public float this[int index]
    {
        get
        {
            unsafe
            {
                float* p = (float*)hArr.ToPointer();
                return *(p + i);
            }

        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator<float> IEnumerable<float>.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    int IReadOnlyCollection<float>.Count
    {
        get { throw new NotImplementedException(); }
    }
}

然后你的容器类被改成:
// developer supplies here with his/hers own array
// or this library uses its own fast C++ wrapper with a [] overload
public IList<T> array { get; set; }

// returns float, int, byte
public T this[int i]
{
    get
    {
        return array[i]; // float[], int[], FloatArr, ...
    }
    set
    {
        array[i] = value;
    }
}

或者

// developer supplies here with his/hers own array
// or this library uses its own fast C++ wrapper with a [] overload
public IReadOnlyList<T> array { get; set; }

// returns float, int, byte
public T this[int i]
{
    get
    {
        return array[i]; // float[], int[], FloatArr, ...
    }
}

那么使用接口,它们将是兼容的。然后我需要让任何未来的结构类型实现IList? - huseyin tugrul buyukisik
有三种可能的接口可供选择:IListIList<T>IReadOnlyList<T>,任何一种都可以使用,在.net中内置的Array类型实现了这3个接口。您只需要选择其中一个,并使您将用作后备存储的类实现该接口即可。 - Scott Chamberlain
我觉得我在面向对象编程方面还是个新手。这是一个不好的设计吗? - huseyin tugrul buyukisik
我还发布了第二个答案,它的编码更简单,但使用接口会比使用“dynamic”慢得多。如果有选择的话,应该使用接口而不是“dynamic”。编辑:使用接口并不是一个坏设计。它们的整个目的就是让你不需要进行强制转换以获取所需的功能。 - Scott Chamberlain

1

除了实现 IList<T>IReadOnlyList<T>,您还可以使用 dynamic 关键字进行运行时绑定,而不是编译时绑定。但是,这将比使用 IList<T>IReadOnlyList<T> 的性能要差得多。

// developer supplies here with his/hers own array
// or this library uses its own fast C++ wrapper with a [] overload
public dynamic array { get; set; }

// returns float, int, byte
public T this[int i]
{
    get
    {
        return array[i]; // float[], int[], FloatArr[], ...
    }
    set 
    {
        array[i] = value;
    }
}

我需要我的库与3.5版本(或有时是2.0版本)兼容。因此,动态可能对我不利? - huseyin tugrul buyukisik
使用接口会比较好。 - Scott Chamberlain
非常感谢(Jon Skeet 之前说过,但当时我没有理解)。 - huseyin tugrul buyukisik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接