【问题标题】:Cast class into another class or convert class to another将类转换为另一个类或将类转换为另一个
【发布时间】:2011-04-10 00:29:52
【问题描述】:

我的问题显示在这段代码中

我有这样的课

public class MainCS
{
  public int A;
  public int B;
  public int C;
  public int D; 
}

public class Sub1
{
  public int A;
  public int B;
  public int C;
}


public void MethodA(Sub1 model)
{
  MainCS mdata = new MainCS() { A = model.A, B = model.B, C = model.C };   

  // is there a way to directly cast class Sub1 into MainCS like that    
  mdata = (MainCS) model;    
}

【问题讨论】:

  • 已经为此目的编写了现有的轻量级映射器库。他们处理更多的边缘情况。你可以谷歌一下。
  • 第一个简单的解决方案:automapper.org
  • 为什么不编写 sub1 构造函数接受 maincs 作为参数并在使用 maincs 的字段初始化 sub1 的字段后返回新的 sub1?

标签: c# casting class type-conversion


【解决方案1】:

我开发了一个类 ObjectChanger,其中包含函数 ConvertToJson、DeleteFromJson、AddToJson 和 ConvertToObject。这些函数可用于将 C# 对象转换为 JSON,然后可以相应地删除或添加属性。之后,可以使用 ConvertToObject 函数将调整后的 JSON 对象简单地转换为新对象。在下面的示例代码中,“AtoB”类在其 GetAtoB() 函数中使用了 ObjectChanger:

using System.Collections.Generic;
using Newtonsoft.Json;
using Nancy.Json;
namespace YourNameSpace
{
public class A
{
    public int num1 { get; set; }
    public int num2 { get; set; }
    public int num3 { get; set; }
}
public class B//remove num2 and add num4
{
    public int num1 { get; set; }
    public int num3 { get; set; }
    public int num4 { get; set; }
}
/// <summary>
/// This class utilizes ObjectChanger to illustrate how
/// to convert object of type A to one of type B
/// by converting A to a Json Object, manipulating the JSON
/// and then converting it to object of type B
/// </summary>
public class AtoB
{
    public dynamic GetAtoB()
    {
        A objectA = new A
        {
            num1 =1, num2 =2,num3 =3
        };
        //convert "objectA" to JSON object "jsonA"
        dynamic jsonA = ObjectChanger.ConvertToJson(objectA);
        //remove num2 from jsonA
        ObjectChanger.DeleteFromJson(jsonA, "num2");
        //add property num4 with value 4 to jsonA
        ObjectChanger.AddToJson(jsonA, "num4", 4);

        B objectB = ObjectChanger.ConvertToObject<B>(jsonA);

        return objectB;
       
        //note: Above DeleteFromJson not needed if the 
        //property(e.g "num2") doesn't exist in objectB   
        //the jsonA will still keep the num2 but when
        //ConvertToObject is called the objectB will only get 
        //populated with the relevant fields.
    }
}
public class ObjectChanger
{
    /// <summary>
    /// Converts a provided class to JsonObject
    /// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
    /// </summary>
    public static dynamic ConvertToJson(dynamic providedObj)
    {
        return JsonConvert.DeserializeObject<System.Dynamic.ExpandoObject>
               (JsonConvert.SerializeObject(providedObj));
        
    }
    /// <summary>
    /// Deletes Property from Json Object
    /// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
    /// ((IDictionary<string, object>)r).Remove("keyvalue");
    /// </summary>
    public static dynamic DeleteFromJson(dynamic providedObj, string keyvalue)
    {
        ((IDictionary<string, object>)providedObj).Remove(keyvalue);
        return providedObj;
    }
    /// <summary>
    /// Adds Property to provided Json Object
    /// </summary>
    /// <param name="providedObj"></param>
    /// <param name="key"></param>
    /// <param name="keyvalue"></param>
    /// <returns>Returns updated Object</returns>
    public static dynamic AddToJson(dynamic providedObj, string key, 
    dynamic keyvalue)
    {
        ((IDictionary<string, object>)providedObj).Add(key, keyvalue);
        return providedObj;
    }
    /// <summary>
    /// Converts provided object providedObj 
    /// to an object of type T
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="providedObj"></param>
    /// <returns></returns>
    public static T ConvertToObject<T>(dynamic providedObj)
    {
        var serializer = new JavaScriptSerializer();
        var json = serializer.Serialize(providedObj);
        var c = serializer.Deserialize<T>(json);
        return c;
    }
}
}

【讨论】:

    【解决方案2】:

    我尝试在目标类型包含源类型中不存在的属性的情况下使用转换扩展(请参阅https://stackoverflow.com/users/247402/stacker)。它没有用,我不知道为什么。我重构了以下适用于我的情况的扩展:

    public static T Casting<T>(this Object source)
    {
        Type sourceType = source.GetType();
        Type targetType = typeof(T);
        var target =  Activator.CreateInstance(targetType, false);
        var sourceMembers = sourceType.GetMembers()
            .Where(x => x.MemberType  == MemberTypes.Property)
            .ToList();
        var targetMembers = targetType.GetMembers()
            .Where(x => x.MemberType == MemberTypes.Property)
            .ToList();
        var members = targetMembers
            .Where(x => sourceMembers
                .Select(y => y.Name)
                    .Contains(x.Name));
        PropertyInfo propertyInfo;
        object value;
        foreach (var memberInfo in members)
        {
            propertyInfo = typeof(T).GetProperty(memberInfo.Name);
            value = source.GetType().GetProperty(memberInfo.Name).GetValue(source, null);
            propertyInfo.SetValue(target, value, null);
        }
        return (T)target;
    }
    

    请注意,我更改了扩展名,因为 Name Cast 与 Linq 的结果冲突。帽子提示https://stackoverflow.com/users/2093880/usefulbee

    【讨论】:

      【解决方案3】:

      使用JSON序列化和反序列化:

      using Newtonsoft.Json;
      
      Class1 obj1 = new Class1();
      Class2 obj2 = JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj1));
      

      或者:

      public class Class1
      {
          public static explicit operator Class2(Class1 obj)
          {
              return JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj));
          }
      }
      

      然后允许你做类似的事情

      Class1 obj1 = new Class1();
      Class2 obj2 = (Class2)obj1;
      

      【讨论】:

      • 是的,使用 Newtonsoft json 序列化器非常简单高效。有 .Net 序列化器,但我发现 Newtonsoft 比 .Net json 序列化器做得更好。我发现这个链接给出了一个简短的比较newtonsoft.com/json/help/html/JsonNetVsDotNetSerializers.htm
      • 这似乎是最新的并且最适合我的需要。赞成!
      • 这正是我所需要的,我试图将从 api 调用返回的 json 对象转换为强类型,永远不要将它与类类型放在一起,很棒的解决方案!
      【解决方案4】:
      var obj =  _account.Retrieve(Email, hash);
      
      AccountInfoResponse accountInfoResponse = new AccountInfoResponse();
      
      if (obj != null)
      {               
         accountInfoResponse = 
         JsonConvert.
         DeserializeObject<AccountInfoResponse>
         (JsonConvert.SerializeObject(obj));
      }
      

      image description

      【讨论】:

        【解决方案5】:

        使用此代码,您可以将任何类对象复制到另一个具有相同名称和相同类型属性的类对象。

        JavaScriptSerializer JsonConvert = new JavaScriptSerializer(); 
        string serializeString = JsonConvert.Serialize(objectEntity);
        objectViewModel objVM = JsonConvert.Deserialize<objectViewModel>(serializeString);
        

        【讨论】:

          【解决方案6】:

          这里有一些很好的答案,我只是想在这里添加一点类型检查,因为我们不能假设如果存在同名的属性,它们就属于同一类型。这是我的提议,它扩展了之前非常出色的答案,因为我遇到了一些小故障。

          在这个版本中,我允许消费者指定要排除的字段,并且默认情况下排除任何数据库/模型特定的相关属性。

              public static T Transform<T>(this object myobj, string excludeFields = null)
              {
                  // Compose a list of unwanted members
                  if (string.IsNullOrWhiteSpace(excludeFields))
                      excludeFields = string.Empty;
                  excludeFields = !string.IsNullOrEmpty(excludeFields) ? excludeFields + "," : excludeFields;
                  excludeFields += $"{nameof(DBTable.ID)},{nameof(DBTable.InstanceID)},{nameof(AuditableBase.CreatedBy)},{nameof(AuditableBase.CreatedByID)},{nameof(AuditableBase.CreatedOn)}";
          
                  var objectType = myobj.GetType();
                  var targetType = typeof(T);
                  var targetInstance = Activator.CreateInstance(targetType, false);
          
                  // Find common members by name
                  var sourceMembers = from source in objectType.GetMembers().ToList()
                                            where source.MemberType == MemberTypes.Property
                                            select source;
                  var targetMembers = from source in targetType.GetMembers().ToList()
                                            where source.MemberType == MemberTypes.Property
                                            select source;
                  var commonMembers = targetMembers.Where(memberInfo => sourceMembers.Select(c => c.Name)
                      .ToList().Contains(memberInfo.Name)).ToList();
          
                  // Remove unwanted members
                  commonMembers.RemoveWhere(x => x.Name.InList(excludeFields));
          
                  foreach (var memberInfo in commonMembers)
                  {
                      if (!((PropertyInfo)memberInfo).CanWrite) continue;
          
                      var targetProperty = typeof(T).GetProperty(memberInfo.Name);
                      if (targetProperty == null) continue;
          
                      var sourceProperty = myobj.GetType().GetProperty(memberInfo.Name);
                      if (sourceProperty == null) continue;
          
                      // Check source and target types are the same
                      if (sourceProperty.PropertyType.Name != targetProperty.PropertyType.Name) continue;
          
                      var value = myobj.GetType().GetProperty(memberInfo.Name)?.GetValue(myobj, null);
                      if (value == null) continue;
          
                      // Set the value
                      targetProperty.SetValue(targetInstance, value, null);
                  }
                  return (T)targetInstance;
              }
          

          【讨论】:

          • 只要你的对象中没有 ENUMS 作为属性,它就可以工作
          【解决方案7】:

          他想说的是:

          “如果您有两个类共享大部分相同的属性,您可以将对象从类a 转换为类b 并通过共享属性名称自动让系统理解分配?”

          选项1:使用反射

          缺点:它会比你想象的更慢。

          选项 2:让一个类从另一个类派生,第一个类具有公共属性,其他类是该类的扩展。

          缺点:耦合!如果您在应用程序中为两层执行此操作,那么这两层将被耦合!

          假设有:

          class customer
          {
              public string firstname { get; set; }
              public string lastname { get; set; }
              public int age { get; set; }
          }
          class employee
          {
              public string firstname { get; set; }
              public int age { get; set; } 
          }
          

          现在这里是对象类型的扩展:

          public static T Cast<T>(this Object myobj)
          {
              Type objectType = myobj.GetType();
              Type target = typeof(T);
              var x = Activator.CreateInstance(target, false);
              var z = from source in objectType.GetMembers().ToList()
                  where source.MemberType == MemberTypes.Property select source ;
              var d = from source in target.GetMembers().ToList()
                  where source.MemberType == MemberTypes.Property select source;
              List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
                 .ToList().Contains(memberInfo.Name)).ToList();
              PropertyInfo propertyInfo;
              object value;
              foreach (var memberInfo in members)
              {
                  propertyInfo = typeof(T).GetProperty(memberInfo.Name);
                  value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj,null);
          
                  propertyInfo.SetValue(x,value,null);
              }   
              return (T)x;
          }  
          

          现在你可以这样使用它:

          static void Main(string[] args)
          {
              var cus = new customer();
              cus.firstname = "John";
              cus.age = 3;
              employee emp =  cus.Cast<employee>();
          }
          

          方法转换检查两个对象之间的公共属性并自动进行分配。

          【讨论】:

          • 不错的解决方案,但正如您所说,开销和复杂性:)
          • 我猜你错过了使用'z'变量。它应该在初始化“成员”变量时使用,即 List members = z.Where(memberInfo => d.Select(c => c.Name) .ToList().Contains(memberInfo.Name)).ToList( );
          • 扩展方法 Cast 会与 System.Linq Enumerable.Cast 发生冲突,并可能导致难以调试的错误。建议更改名称。
          • Cast 方法是 option1,因此使用反射,我理解正确吗?
          【解决方案8】:

          通过使用以下代码,您可以将任何类对象复制到另一个具有相同名称和相同类型属性的类对象。

          public class CopyClass
          {
              /// <summary>
              /// Copy an object to destination object, only matching fields will be copied
              /// </summary>
              /// <typeparam name="T"></typeparam>
              /// <param name="sourceObject">An object with matching fields of the destination object</param>
              /// <param name="destObject">Destination object, must already be created</param>
              public static void CopyObject<T>(object sourceObject, ref T destObject)
              {
                  //  If either the source, or destination is null, return
                  if (sourceObject == null || destObject == null)
                      return;
          
                  //  Get the type of each object
                  Type sourceType = sourceObject.GetType();
                  Type targetType = destObject.GetType();
          
                  //  Loop through the source properties
                  foreach (PropertyInfo p in sourceType.GetProperties())
                  {
                      //  Get the matching property in the destination object
                      PropertyInfo targetObj = targetType.GetProperty(p.Name);
                      //  If there is none, skip
                      if (targetObj == null)
                          continue;
          
                      //  Set the value in the destination
                      targetObj.SetValue(destObject, p.GetValue(sourceObject, null), null);
                  }
              }
          }
          

          调用方法喜欢,

          ClassA objA = new ClassA();
          ClassB objB = new ClassB();
          
          CopyClass.CopyObject(objOfferMast, ref objB);
          

          它将objA复制到objB

          【讨论】:

          • 请注意,如果您使用此解决方案,当类的属性名称相同但类型不同时,您可能会遇到问题。例如:public class A { public int Age{get;set;}}public class B { public string Age{get;set;}} 将抛出异常,如果您尝试从 A 转换为 B
          • 这种类型的转换也可能导致一些巨大的性能问题。小心使用它,当然不要在 for 循环中使用它
          【解决方案9】:

          您可以将您的班级结构更改为:

          public class maincs : sub1
          {
             public int d; 
          }
          
          public class sub1
          {
             public int a;
             public int b;
             public int c;
          }
          

          然后你可以保留一个 sub1 的列表并将其中一些转换为 mainc。

          【讨论】:

          • 这也不能编译。也许你忘记了class 关键字
          • 糟糕,这就是我复制/粘贴的结果。
          【解决方案10】:

          您可以为强制转换运算符提供显式重载:

          public static explicit operator maincs(sub1 val)
          {
              var ret = new maincs() { a = val.a, b = val.b, c = val.c };
              return ret;
          }
          

          另一种选择是使用具有 a、b 和 c 属性的接口并在两个类上实现该接口。然后将方法的参数类型设置为接口而不是类。

          【讨论】:

            【解决方案11】:

            您已经定义了转换,如果您希望能够投射,只需将其更进一步。例如:

            public class sub1
            {
                public int a;
                public int b;
                public int c;
            
                public static explicit operator maincs(sub1 obj)
                {
                    maincs output = new maincs() { a = obj.a, b = obj.b, c = obj.c };
                    return output;
                }
            }
            

            然后允许你做类似的事情

            static void Main()
            {
                sub1 mySub = new sub1();
                maincs myMain = (maincs)mySub;
            }
            

            【讨论】:

            • 比选择的答案还要好
            • 很好的答案。谢谢。
            • 使用您的代码使用确切的道具从一个类转换/转换到另一个类是否有任何影响?
            • 这个问题实际上是非常隐含的。您正在覆盖 C 中一个非常常见的操作。查看这是自定义强制转换的唯一方法是查看类本身。因此,当您大规模执行此操作时,这实际上变得不可读。它欺骗了阅读它的开发人员。
            • 如果您不拥有 sub1 类,您如何做到这一点?有没有办法在 maincs 类中做到这一点?
            猜你喜欢
            • 2022-08-18
            • 1970-01-01
            • 2011-03-15
            • 1970-01-01
            • 2018-06-13
            • 1970-01-01
            • 1970-01-01
            • 2022-12-21
            • 1970-01-01
            相关资源
            最近更新 更多