C# Generics - невозможно преобразовать 'byte []' в 'T' - PullRequest
0 голосов
/ 28 февраля 2020

Мне нужна ваша помощь с использованием обобщенного c класса и методов в C#. Когда я вызываю EnqueObject метод в ProduceData , в строке с EnqueueObject (block) , возникает ошибка: не может преобразовать 'byte []' в 'T' . Буду признателен за любой совет.

(я упростил свой код, потому что я считаю, что моя проблема - нечто действительно основополагающее c).

class CompressingProducer<T>
{
    Queue<T> _queue;

    public void ProduceData(object fileInputStream)
    {
        byte[] block = new byte[Settings.blockSize];
        int bytesRead;

        while ((bytesRead = ((Stream)fileInputStream).Read(block, 0, Settings.blockSize)) > 0)     
        {
            EnqueueObject(block);   
            block = new byte[Settings.blockSize];
        }
    }

    private void EnqueueObject(T data) 
    {
        _queue.Enqueue(data);
    }
}

ОБНОВЛЕНИЕ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;

namespace GZipTestProject
{
    class CompressingProducer<T>
    {           
        Thread _producerThread;
        readonly object _lock;
        Queue<T> _queue;

        /// <summary>
        /// Reads data in chunks (as byte[]) or as CompressedData objects from the file stream and inserts them into a queue
        /// </summary>
        /// <param name="fileInputStream">Connected to the file from which data will be read</param>
        public CompressingProducer(Stream fileInputStream) {
            _lock = ProducerConsumer<T>.getLock();
            _queue = ProducerConsumer<T>.getQueue();

            _producerThread = new Thread(ProduceData);
            _producerThread.Start(fileInputStream);
        }

        /// Takes a file input stream parameter from which data will be read and put into a queue for a consumer.
        /// The parameter of this method must be object because it will be passed as a delegate to a new thread.
        public void ProduceData(object fileInputStream)
        {
            if (GZipTest.GetActionType() == ActionType.Compress) {  

                byte[] block = new byte[Settings.blockSize];
                int bytesRead;

                while ((bytesRead = ((Stream)fileInputStream).Read(block, 0, Settings.blockSize)) > 0)     
                {                    
                    if (bytesRead < block.Length)
                    {
                        byte[] block2 = new byte[bytesRead];
                        Array.Copy(block, block2, bytesRead);
                        block = block2;
                    }

                    EnqueueObject(block);   // put the data block into the queue
                    block = new byte[Settings.blockSize];
                }
        }

        private void EnqueueObject(T data) //byte[] block  or CompressedData   
        {
            lock (_lock)
            {
                while (_queue.Count >= Settings.maxQueueSize)
                {
                    Monitor.Wait(_lock);     // suspends the whole main thread of the application
                }

                _queue.Enqueue(data);

                if (_queue.Count == 1)
                {
                    // wake up any blocked dequeue, i.e. the consumer thread
                    Monitor.PulseAll(_lock);
                }
            }
        }
    }
}

Я понял, что вам нужно дополнительное объяснение того, как должна работать программа. Он должен использоваться для сжатия и распаковки. Сначала он читает файл по блокам (байтовым массивам), затем сжимает блоки, помещает их в объекты моего собственного класса CompressedData и сериализует их в один файл. Во время распаковки объекты десериализуются, а сжатые байтовые массивы распаковываются в новый файл. Он использует шаблон «производитель-потребитель», поэтому «производитель» помещает элементы в очередь, сама очередь и «потребитель» принимают элементы и обрабатывают требуемую операцию (сжатие / декомпрессия). Я хочу использовать мой класс CompressingProducer в качестве «производителя» операций, сжатия и распаковки, это означает, что он ставит в очередь байтовые массивы во время сжатия, и объекты моего класса CompressedData во время распаковки. Вот почему я пытаюсь использовать дженерики.

Ответы [ 3 ]

0 голосов
/ 28 февраля 2020

Нельзя указывать T как реальный класс в классе generi c, просто выведите функцию ProduceData из класса

0 голосов
/ 28 февраля 2020

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

Когда вы объявляете переменную CompressingProducer, вам нужно передать параметр типа

CompressingProducer<byte[]> producer = new CompressingProducer<byte[]>();

T = byte [] . В этом случае T совпадает с типом byte [], но мы говорим о обобщенных c типах, которые могут быть любыми типами, а не только byte [].

Рассмотрим объявление следующего за T сейчас: string

CompressingProducer<string> producer = new CompressingProducer<string>();

T = строка , но внутри метода CompressingProducer.EnqueueObject требуется параметр T, параметр не равен байту [], равен строке.

Обобщения помогают обрабатывать любой тип , но тип зависит от типа, переданного в качестве параметра при объявлении переменной. Проблема состоит в том, чтобы относиться к типу T как к любому типу внутри класса CompressingProducer. T - это тип, определяемый параметром при объявлении любой переменной CompressingProducer. По этой причине вы не можете передавать никакое значение методу EnqueueObject, только значения типа T.

Всегда рассматривайте T как виртуальный / вымышленный / воображаемый тип, который является реальным типом, когда передаете тип как параметр при создании переменная.

Если вам нужно обработать любой тип в очереди, используйте класс объекта в параметре типа, в C# любой класс исключает объект, наследуемый от объекта.

class CompressingProducer
    {

        Queue<Object> _queue;

        public void ProduceData(object fileInputStream)
        {
            byte[] block = new byte[Settings.blockSize];
            int bytesRead;

            while ((bytesRead = ((Stream)fileInputStream).Read(block, 0, Settings.blockSize)) > 0)
            {
                EnqueueObject(block);//Needs object of T type.
                block = new byte[Settings.blockSize];
            }
        }

        private void EnqueueObject(Object data)
        {
            _queue.Enqueue(data);
        }
    }

Мое окончательное решение - изменить метод ProduceData , добавить еще один параметр типа Fun c, этот параметр является делегатом для преобразования из байта [] в тип, переданный в качестве параметра.

Два примера использования:
T = строка
T = Персона (Тип примера)

   class CompressingProducer<T>
    {

        Queue<T> _queue;

        public void ProduceData(Stream fileInputStream, Func<byte[], T> convert)
        {
            byte[] block = new byte[Settings.blockSize];
            int bytesRead;

            while ((bytesRead = ((Stream)fileInputStream).Read(block, 0, Settings.blockSize)) > 0)
            {
                EnqueueObject(convert(block));//Using convert function, from byte[] ==> to T 
                block = new byte[Settings.blockSize];
            }
        }

        private void EnqueueObject(T data)
        {
            _queue.Enqueue(data);
        }
    }


    class Program
    {
        public static string ConvertData(byte[] data)
        {
            return Encoding.UTF8.GetString(data);
        }

        class Person
        {
            public long Id { get; set; }
            public String Name { get; set; }

            public byte[] ToBytes()
            {
                //this is aproach example, Needs more improvements
                return BitConverter.GetBytes(Id).Concat(Encoding.UTF8.GetBytes(Name).Take(Settings.blockSize - 8/* long type size of Id */)).ToArray(); //byte[] of blockSize size
            }

            public static Person ToPerson(byte[] bytes)
            {
                return new Person
                {
                    Id = BitConverter.ToInt64(bytes.Take(8).ToArray()),
                    Name = Encoding.UTF8.GetString(bytes.Skip(8).ToArray())
                };
            }
        }
        static void Main(string[] args)
        {
            //EXAMPLE 1
            CompressingProducer<string> producer = new CompressingProducer<string>();
            using (Stream filestream = File.OpenRead("path to file"))
            {
                //ProduceData(Stream fileInputStream, Func<byte[], string> convert), T now is string type.
                producer.ProduceData(filestream, data => Encoding.UTF8.GetString(data));//Using lambda expression. 
                producer.ProduceData(filestream, ConvertData);//Using delegate. //Same result as lambda.
            }

            //EXAMPLE 2
            CompressingProducer<Person> personProducer = new CompressingProducer<Person>();
            using (Stream filestream = File.OpenRead("path to file containing Person Blocks"))
            {
                //ProduceData(Stream fileInputStream, Func<byte[], Person> convert), T now is Person type.
                personProducer.ProduceData(filestream, data => Person.ToPerson(data));//Using lambda expression. 
            }

            Console.ReadLine();
        }
    }
0 голосов
/ 28 февраля 2020

ProduceData() тесно связан с byte[], поэтому тип элементов в Queue<T> должен быть таким же:

class CompressingProducer
{
    private readonly Queue<byte[]> _queue;

    public void ProduceData(object fileInputStream)
    {
        byte[] block = new byte[Settings.blockSize];

        while (((Stream)fileInputStream).Read(block, 0, Settings.blockSize) > 0)
        {
            EnqueueObject(block);
            block = new byte[Settings.blockSize];
        }
    }

    private void EnqueueObject(byte[] block)
    {
        _queue.Enqueue(block);
    }
}

Обновление

Чтобы сохранить генерик c вы должны сделать что-то вроде этого:

public abstract class CompressingProducer<T>
{
    public Queue<T> Queue { get; set; }

    public abstract void ProduceData(object fileInputStream);

    protected void EnqueueObject(T element)
    {
        Queue.Enqueue(element);
    }
}

public class CompressingProducerByByteArray : CompressingProducer<byte[]>
{
    public override void ProduceData(object fileInputStream)
    {
        byte[] block = new byte[Settings.blockSize];

        while (((Stream)fileInputStream).Read(block, 0, Settings.blockSize) > 0)
        {
            EnqueueObject(block);
            block = new byte[Settings.blockSize];
        }
    }
}
...