如何将对象转换为 Type 变量指定的类型

How to cast object to type specified by Type variable

我正在寻找一种方法,将 object 变量转换为类型为 Type 的其他变量指定的通用类型参数的类型。

我仅限于 .NET 3.5,所以无法使用 dynamic :( 这里的主要思想是我可以访问字典:

Dictionary<Type, object> data;

该词典的数据仅以以下形式添加:

data.Add(T, new DataSub<T>(someValueOfTypeT));

问题是,当我试图逆转这个过程时:

foreach(var dataType in data.Keys) {
  var dataValue = data[dataType];
  ProcessDataValue(dataType, dataValue);
}

现在的问题是如何设法将对象转换为 DataSub?

简化DataSub.cs:

public class DataSub<T>
{
  private T _cache;
  public T Value {
    get { return _cache; }
    set { _cache = value; }
  }
}

它如何在 ProcessDataValue 中工作:

public void ProcessDataValue(Type dataType, object dataValue)
{
  var data = dataValue as DataSub<dataType>;
  if (data == null) return;
  AddProcessedDataValue(dataType, data.Value.ToString());
}

如果您可以对您发布的 类 和 if 进行最小的更改 - 如您的示例所示 - 您将如何处理DataSub.Value 正在调用 ToString,也许您可​​以通过

获得您需要的结果
    public interface IDataSub {
        bool MatchesType(Type t);

        object GetValue();
    }

    public class DataSub<T> : IDataSub {
        private T _cache;
        public T Value {
            get { return _cache; }
            set { _cache = value; }
        }

        public bool MatchesType(Type t) {
            return typeof(T) == t; // or something similar, in order to handle inheritance
        }

        public object GetValue() {
            return Value;
        }
    }

    public class Client {
        Dictionary<Type, IDataSub> data = new Dictionary<Type, IDataSub>() ;


        public void AddData<T>(T someValueOfTypeT) {
            data.Add(typeof(T), new DataSub<T> { Value = someValueOfTypeT });
        }


        public void UseData() {
            foreach(var dataType in data.Keys) {
                var dataValue = data[dataType];
                ProcessDataValue(dataType, dataValue);
            }
        }

        public void ProcessDataValue(Type dataType, IDataSub dataValue)
        {
            if(dataValue.MatchesType(dataType))
                AddProcessedDataValue(dataType, dataValue.GetValue().ToString());
        }
    }

如果 DataSub.Value.ToString 的用法只是一个例子,而在 现实世界中 您需要使用其访问 DataSub.Value输入 T,您应该对您的代码进行更广泛的修改。

您如何看待以下方法?这是我喜欢调用 set of responsibility (I wrote the linked post about this topic), a variation of GoF's chain of responsibility:

模式的应用
    public interface IDataSub {
        object GetValue();
    }

    public class DataSub<T> : IDataSub {
        private T _cache;
        public T Value {
            get { return _cache; }
            set { _cache = value; }
        }

        public object GetValue() {
            return Value;
        }
    }

    public interface IDataHandler {
        bool CanHandle(Type type);

        void Handle(object data);
    }

    public class Client {
        private readonly Dictionary<Type, IDataSub> data = new Dictionary<Type, IDataSub>();
        private readonly IList<IDataHandler> handlers = new List<IDataHandler>();


        public void AddData<T>(T someValueOfTypeT) {
            data.Add(typeof(T), new DataSub<T> { Value = someValueOfTypeT });
        }

        public void RegisterHandler(IDataHandler handler) {
            handlers.Add(handler);
        }


        public void UseData() {
            foreach(var dataType in data.Keys) {
                handlers.FirstOrDefault(h => h.CanHandle(dataType))?.Handle(data[dataType].GetValue());
            }
        }

        // Lambda-free version
//        public void UseData() {
//            foreach(var dataType in data.Keys) {
//                for (int i = 0; i < handlers.Count; i++) {
//                    if (handlers[i].CanHandle(dataType)) {
//                        handlers[i].Handle(data[dataType].GetValue());
//                        break; // I don't like breaks very much...
//                    }
//                }
//            }
//        }
    }

    class StringDataHandler : IDataHandler {
        public bool CanHandle(Type type) {
            // Your logic to check if this handler implements logic applyable to instances of type
            return typeof(string) == type;
        }

        public void Handle(object data) {
            string value = (string) data;
            // Do something with string
        }
    }

    class IntDataHandler : IDataHandler {
        public bool CanHandle(Type type) {
            // Your logic to check if this handler implements logic applyable to instances of type
            return typeof(int) == type;
        }

        public void Handle(object data) {
            int value = (int) data;
            // Do something with int
        }
    }

这种方法允许您将数据存储和数据迭代逻辑与特定于不同数据类型的数据处理逻辑分离:IDataHandler 的实现知道它们可以处理什么类型的数据并投射通用 object 对所需类型的引用。如果您愿意,可以将 CanHandle 方法合并到 Handle 方法中,删除前一个方法并将 UseData 更改为

public void UseData() {
    foreach(var dataType in data.Keys) {
        foreach(var handler in handlers) {
            handler.Handle(dataType, data[dataType].GetValue())
        }
    }
}

和处理程序实现

class IntDataHandler : IDataHandler {
        public void Handle(Type dataType, object data) {
            if(typeof(int) == type) {
                int value = (int) data;
                // Do something with int
            }
        }
    }

这个变体稍微更安全一些,因为在第一个变体中已经可以调用 Handle 方法而无需先前调用 CanHandle.

如果您喜欢这种方法,可以提出来,简化您的数据结构并将 dataIDictionary 转换为 IList :

    public interface IDataSub {
        object GetValue();
    }

    public class DataSub<T> : IDataSub {
        private T _cache;
        public T Value {
            get { return _cache; }
            set { _cache = value; }
        }

        public object GetValue() {
            return Value;
        }
    }

    public interface IDataHandler {
        bool CanHandle(object data);

        void Handle(object data);
    }

    public class Client {
        private readonly IList<IDataSub> data = new List<IDataSub>();
        private readonly IList<IDataHandler> handlers = new List<IDataHandler>();


        public void AddData<T>(T someValueOfTypeT) {
            data.Add(new DataSub<T> { Value = someValueOfTypeT });
        }

        public void RegisterHandler(IDataHandler handler) {
            handlers.Add(handler);
        }


        public void UseData() {
            foreach(var dataItem in data) {
                var value = dataItem.GetValue();
                handlers.FirstOrDefault(h => h.CanHandle(value))?.Handle(value);
            }
        }

        // Lambda-free version as above...


    class StringDataHandler : IDataHandler {
        public bool CanHandle(object data) {
            // Your logic to check if this handler implements logic applyable to instances of String
            return data is string;
        }

        public void Handle(object data) {
            string value = (string) data;
            // Do something with string
        }
    }

    class IntDataHandler : IDataHandler {
        public bool CanHandle(Type type) {
            // Your logic to check if this handler implements logic applyable to instances of int
            return type is int;
        }

        public void Handle(object data) {
            int value = (int) data;
            // Do something with int
        }
    }

CanHandle-free 变体也可以简化 IDataHandler 接口及其在这种情况下的实现...

希望我的回答能帮助您解决设计场景问题;我基于我非常喜欢的方法构建它,因为它允许将特定于子类型的逻辑应用于不同类的实例,前提是它们共享一个公共超类(如我的代码示例中的 object)。