2013-12-17 5 views
7

ВОПРОСC# Явный оператор и объект

Пожалуйста, обратите внимание на код первого.

Вот мой пользовательский класс:

public class float2D 
{ 
    public float X { get; private set; } 
    public float Y { get; private set; } 

    public float2D(float x, float y) 
    { 
     this.X = x; 
     this.Y = y; 
    } 

    public static explicit operator Point(float2D x) 
    { 
     return new Point((int)x.X, (int)x.Y); 
    } 

    ... 
} 

А вот тест код, который я написал:

private void TEST() 
    { 
     float2D V = new float2D(1, 1); 
     Point P = Point.Empty; 
     P = (Point)V; // Works 
     P = (Point)(V as object); // Specified cast is not valid. 
    } 

Как вы можете видеть, что это не удалось преобразовать значение, когда тип значения не известно. Я считаю, что это происходит потому, что он ищет в классе Object для операнда не в реальном типе. Как я могу решить эту проблему?

У меня есть код, где ВСЕ это объект, и он должен позаботиться об этих разговорах.

Скажите, пожалуйста, если у вас есть Идея.


ИЗБЕЖАТЬ DYNAMIC

Ok LET изменить образец, как вы можете увидеть, что я именно хочу сделать и то, что моя ситуация.

Вот класс у меня есть:

class TEST 
    { 
     dynamic OBJECT; 
     public void STORE<T>(ref T V) 
     { 
      this.OBJECT = V; 
     } 

     public T CONVERT<T>() 
     { 
      return (T)this.OBJECT; 
     } 
    } 

А вот тестовый код:

 float2D V = new float2D(1, 1); 
     TEST t = new TEST(); 
     t.STORE(ref V); 
     Point P = t.CONVERT<Point>(); 

Есть ли способ я могу бросить динамический сверху класса и держать его работать? Я действительно хочу избежать .Net4/4.5

+1

См. [Представление и идентификация] (http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx) – Johnbot

+0

Кроме этого; почему вы бросаете X и Y в целые числа? –

+2

@daveL - я бы предположил его, потому что [конструктор для точки] (http://msdn.microsoft.com/en-us/library/s9b80s6c%28v=vs.110%29.aspx) принимает целые числа. – Jamiec

ответ

2

Как указано в других ответах, вы не можете сделать это, потому что вы пытаетесь применить выполнения литья в время компиляции конверсионных операций.

Если вы хотите избежать dynamic, потому что вы не хотите использовать DLR .NET 4.0, вы можете использовать отражение, чтобы найти операторов преобразования самостоятельно. Не могу прокомментировать производительности для однако ваше конкретное приложение делает это:

public static TOutgoing Convert<TOutgoing>(object obj) 
{ 
    Type incomingType = obj.GetType(); 

    MethodInfo conversionOperator = null; 
    foreach(var method in incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public)) 
    { 
     if (
      method.Name == "op_Explicit" && //explicit converter 
      method.ReturnType == typeof(TOutgoing) && //returns your outgoing ("Point") type 
      method.GetParameters().Length == 1 && //only has 1 input parameter 
      method.GetParameters()[0].ParameterType == incomingType //parameter type matches your incoming ("float2D") type 
      ) 
     { 
      conversionOperator = method; 
      break; 
     } 
    } 

    if (conversionOperator != null) 
     return (TOutgoing)conversionOperator.Invoke(null, new object[]{obj}); 

    throw new Exception("No conversion operator found"); 
} 

или для .NET 3.5 с LINQ:

public static TOutgoing Convert<TOutgoing>(object obj) 
{ 
    Type incomingType = obj.GetType(); 

    var conversionOperator = incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public) 
     .Where(m => m.Name == "op_Explicit") 
     .Where(m => m.ReturnType == typeof(TOutgoing)) 
     .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == incomingType) 
     .FirstOrDefault(); 

    if (conversionOperator != null) 
     return (TOutgoing)conversionOperator.Invoke(null, new object[]{obj}); 

    throw new Exception("No conversion operator found"); 
} 

Использование:

float2D V = new float2D(1, 1); 
Point P = Point.Empty; 
P = Convert<Point>(V); //passes 
P = Convert<Point>((V as object)); //passes 

Вы можете добавить «op_Implicit «а также если вы хотите конвертировать через неявные операторы.


Другой вариант, если вы хотите, чтобы избежать отражения, чтобы заранее зарегистрировать функции преобразования, некоторые отливки и Lookups типа, чтобы определить, какой оператор преобразования использовать.

Просто предостережение. В решении есть несколько проблем (безопасность потоков, предположение о том, что существуют функции преобразования, регистрация встречных/дублирующих функций преобразования вызывает ошибки), поэтому используйте на свой страх и риск или используйте его в качестве руководства для изменения, поскольку оно соответствует вашим потребностям.

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

public interface IConverter 
{ 
    object Convert(object incomingObject); 
} 

public class Converter<TIncoming, TOutgoing> : IConverter 
{ 
    private Func<TIncoming, TOutgoing> ConversionFunction; 

    public Converter(Func<TIncoming, TOutgoing> conversionFunction) 
    { 
     this.ConversionFunction = conversionFunction; 
    } 

    public object Convert(object incomingObject) 
    { 
     TIncoming typedIncomingObject = (TIncoming)incomingObject; 
     return ConversionFunction(typedIncomingObject); 
    } 
} 

Затем утилита, вы можете зарегистрировать эти преобразования с помощью:

public static class ConversionUtility 
{ 
    private static Dictionary<Type, Dictionary<Type, IConverter>> Converters = new Dictionary<Type, Dictionary<Type, IConverter>>(); 

    public static void RegisterConversion<TIncoming, TOutgoing>(Func<TIncoming, TOutgoing> conversionFunction) 
    { 
     if (!Converters.ContainsKey(typeof(TIncoming))) 
     { 
      Converters[typeof(TIncoming)] = new Dictionary<Type, IConverter>(); 
     } 

     Converters[typeof(TIncoming)].Add(typeof(TOutgoing), new Converter<TIncoming, TOutgoing>(conversionFunction)); 
    } 

    public static TOutgoing Convert<TOutgoing>(object obj) 
    { 
     Type incomingType = obj.GetType(); 

     IConverter converter = Converters[incomingType][typeof(TOutgoing)]; 

     return (TOutgoing)converter.Convert(obj); 
    } 
} 

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

ConversionUtility.RegisterConversion((float2D obj) => (Point)obj); 

Тогда ваше использование преобразования:

float2D V = new float2D(1, 1); 
Point P = Point.Empty; 
P = ConversionUtility.Convert<Point>(V); //passes 
P = ConversionUtility.Convert<Point>((V as object)); //passes 

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

+0

Спасибо Крису. Это единственный ответ, который затрагивает реальный вопрос. –

2

Это потому, что вы отбрасываете его до object, и у вас нет явного приведения для этого - он не будет подразумевать, что вы хотите, чтобы он был как float2D.

+0

Да Даниал, спасибо за быстрый ответ. Я уже знаю, что. Я спрашивал, можно ли как-то решить эту проблему? Потому что, как я уже говорил, у меня есть код, где все является объектом, и мне нужен способ обработки этих разговоров. –

+0

@SoroushFalahati вы можете использовать дженерики? –

+0

@ Даниал, Да и я тоже тестировал с помощью Generics (для методов). Но есть и другая проблема. Мне нужно сохранить его в переменной. Поэтому, когда я конвертирую Generic Type в Object (тип переменной), я фактически потерял реальный тип данных. Я думаю, что единственный способ - использовать динамический (как переменный тип данных), хотя я не хотел его использовать, потому что мне нравится держать код в .Net2/3.5 –

15

Да, эти две разные вещи. Первая строка:

P = (Point)V; // Works 

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

P = (Point)(V as object); // Specified cast is not valid. 

это ставит ссылку V как object (который мы тривиальные знаем, что есть) - и затем отдельно изгоняют из object в Point.

С Point это номер struct, после чего делается попытка «удалить» эту же ссылку, как если бы она была в коробке Point. Но это не в коробке Point, поэтому ошибка верна. Операторы преобразования не являются полиморфными, и unboxing разрешен только в том случае, если в поле содержится коробочная копия соответствующего типа (caveat: вы можете распаковывать перечисления как целые числа, а целые числа - как перечисления, если совпадают базовые типы).

Тем не менее, даже если Point был class, он по-прежнему терпел бы неудачу с аналогичной ошибкой: следы сохранения ссылок также не работают, если типы несовместимы.

+0

Стоит отметить, что бокс происходит на 'Point', потому что это' struct' – Gusdor

+1

@Gusdor будет редактировать ... –

+0

@MarcGravell Спасибо за эту полезную информацию. У меня есть вопрос, могу ли я как-то попросить во время выполнения использовать другой тип вместо объекта. Скажем, я знаю, что такое настоящий тип, но я сохранил ценность и тип в двух разных переменных. Объект и тип. Могу ли я преобразовать этот объект в тип (я знаю, что это возможно с T, но не уверен, возможно ли это с помощью Type), а затем использовать явное преобразование для его преобразования, например Point? –