Как отделить двойные и строковые элементы, получив неверную ошибку приведения - PullRequest
1 голос
/ 28 января 2020

У меня есть Dictionary<string, object>, у которого есть string и double значение для object. Теперь я хочу выделить оба типа значений в коде ниже. В настоящее время я получаю недопустимая ошибка приведения . Как это сделать?

namespace ConsoleApp26
{
public class Sample
{
    public string SampleName;
    public Dictionary<string, object> SampleValues;
}

public class SampleDivideBetweenDoubleAndStringValue
{
    public string Name { get; set; }
    public double DoubleValue { get; set; }
    public string StringValue { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var samples = new Sample
            {
                SampleName = "test",
                SampleValues = new Dictionary<string, object> { 
                    { "t1",  45.08 }, 
                    { "t2", "A String Value" }, 
                    { "t3",  83 } 
                }
            };

            var tuple = GetTuple(samples);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

        Console.ReadKey();
    }

    private static Tuple<IEnumerable<SampleDivideBetweenDoubleAndStringValue>, IEnumerable<SampleDivideBetweenDoubleAndStringValue>> GetTuple(Sample samples)
    {
        var doubles = (from s in samples.SampleValues
                       select new SampleDivideBetweenDoubleAndStringValue
                       {
                           Name = samples.SampleName,
                           DoubleValue = (double)s.Value
                       });

        var strings = (from s in samples.SampleValues
                       select new SampleDivideBetweenDoubleAndStringValue
                       {
                           Name = samples.SampleName,
                           StringValue = (string)s.Value
                       });

        return new Tuple<
          IEnumerable<SampleDivideBetweenDoubleAndStringValue>, 
          IEnumerable<SampleDivideBetweenDoubleAndStringValue>>(doubles, strings);
    }
}
}

Ответы [ 4 ]

1 голос
/ 28 января 2020

Основная проблема со структурой данных заключается в том, что у вас есть не только string и double, но int:

  var SampleValues = new Dictionary<string, object> { 
    { "t1",  45.08 },             // boxed double value
    { "t2", "A String Value" },   // string value
    { "t3",  83 },                // <- N.B. this is boxed int (83), not double (83.0) value
  };

Casting в штучной упаковке int в double выдает исключение :

  object o = 83;         // int value (83) which is boxed into object

  double d = (double) o; // <- Exception will be thrown here (invalid cast)

Обратите внимание, что преобразование в порядке:

  object o = 83;

  double d = Convert.ToDouble(o); // d == 83.0

Таким образом, вы можете попробовать отфильтровать String элементов, а затем Convert все остальные (значения double и int) в double; в вашем случае:

  var doubles = samples
    .SampleValues       
    .Where(pair => (pair.Value != null) && !(pair.Value is string)) // not string value
    .Select(pair => new {
       Name  = samples.Name,
       Value = Convert.ToDouble(pair.Value), // which we convert to double
     });

  var strings = samples
    .SampleValues       
    .Where(pair => pair.Value is string)
    .Select(pair => new {
       Name  = samples.Name,
       Value = Convert.ToString(pair.Value),
     });
1 голос
/ 28 января 2020

Вы можете использовать Where метод для фильтрации значений по типу, например, (я использовал лямбда-синтаксис)

private static Tuple<IEnumerable<SampleDivideBetweenDoubleAndStringValue>, IEnumerable<SampleDivideBetweenDoubleAndStringValue>> GetTuple(Sample samples)
{
    var doubles = samples.SampleValues.Where(s => s.Value is double).Select(s => new SampleDivideBetweenDoubleAndStringValue
    {
        Name = samples.SampleName,
        DoubleValue = (double)s.Value
    });

    var strings = (samples.SampleValues.Where(s => s.Value is string).Select(s => new SampleDivideBetweenDoubleAndStringValue
    {
        Name = samples.SampleName, StringValue = (string) s.Value
    }));

    return new Tuple<IEnumerable<SampleDivideBetweenDoubleAndStringValue>, IEnumerable<SampleDivideBetweenDoubleAndStringValue>>(doubles, strings);
}

Для синтаксиса запроса в вашем примере вы можно просто использовать

var doubles = from s in samples.SampleValues
        where s.Value is double
        select new SampleDivideBetweenDoubleAndStringValue
        {
            Name = samples.SampleName,
            DoubleValue = (double)s.Value
        };

var strings = from s in samples.SampleValues
        where s.Value is string
        select new SampleDivideBetweenDoubleAndStringValue
        {
            Name = samples.SampleName,
            StringValue = (string)s.Value
        };

Поскольку Linq использует отложенное выполнение, недопустимое исключение приведения в вашем примере будет показано только после того, как вы выполните итерацию IEnumerable. Существует также неясная точка со значением 83, так как это целое число

1 голос
/ 28 января 2020

Вы можете проверить тип объекта до того, как произнесете:

var doubles = (from s in samples.SampleValues
               where s.Value is double
               select new SampleDivideBetweenDoubleAndStringValue
               {
                   Name = samples.SampleName,
                   DoubleValue = (double)s.Value
               });
// same for `string`

Или можете использовать as:

var sampleValues = (from s in samples.SampleValues
                    select new SampleDivideBetweenDoubleAndStringValue
                    {
                        Name = samples.SampleName,
                        DoubleValue = s.Value as double? ?? 0,
                        StringValue = s.Value as string
                    });
0 голосов
/ 28 января 2020

Вам нужен ключ для строк:

private static Tuple<IEnumerable<SampleDivideBetweenDoubleAndStringValue>, IEnumerable<SampleDivideBetweenDoubleAndStringValue>> GetTuple(Sample samples)
{
    var doubles = (from s in samples.SampleValues
                   select new SampleDivideBetweenDoubleAndStringValue
                   {
                       Name = samples.SampleName,
                       DoubleValue = (double)s.Value
                   });

    var strings = (from s in samples.SampleValues
                   select new SampleDivideBetweenDoubleAndStringValue
                   {
                       Name = samples.SampleName,
                       StringValue = (string)s.Key
                   });

    return new Tuple<IEnumerable<SampleDivideBetweenDoubleAndStringValue>, IEnumerable<SampleDivideBetweenDoubleAndStringValue>>(doubles, strings);
}

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

РЕДАКТИРОВАТЬ Я не видел, чтобы это было , Василий прав. Может быть, я должен удалить свой ответ, но я уйду, чтобы научить себя не отвечать, прежде чем я прочту полный вопрос.

...