从对象中删除空属性

4

我有一个类,在其中我现在有三个属性。如果在对象中任何一个是空或null,那么我想从对象中删除它。以下是我的代码。

public class TestClass
{
    public string Name { get; set; }
    public int ID { get; set; }
    public DateTime? DateTime { get; set; }
    public string Address { get; set; }
}
       TestClass t=new TestClass();
        t.Address="address";
        t.ID=132;
        t.Name=string.Empty;
        t.DateTime=null;

现在我想要TestClass对象,但是该对象中的Name和DateTime属性不应存在,这有可能吗? 请帮帮我。


1
“Remove”是什么意思? - Lasse V. Karlsen
1
我不希望在 TestClass 对象中有那个属性。 - Dhaval Patel
除非您永久删除它以适用于所有情况,否则无法删除属性。但是,您可以创建多个类,在类层次结构中,其中一个类具有该属性,而另一个类没有。 - Lasse V. Karlsen
1
好的,我正在使用无模式数据库,因此空值和空字符串也会在数据库中存储空格,这就是原因。 - Dhaval Patel
我不想永久从TestClass中删除它。 - Dhaval Patel
显示剩余3条评论
5个回答

7

我感到无聊,于是在LINQPad中得到了这个结果。

void Main()
{
    TestClass t=new TestClass();
    t.Address="address";
    t.ID=132;
    t.Name=string.Empty;
    t.DateTime=null;

    t.Dump();
    var ret = t.FixMeUp();
    ((object)ret).Dump();
}

public static class ReClasser
{
    public static dynamic FixMeUp<T>(this T fixMe)
    {
        var t = fixMe.GetType();
        var returnClass = new ExpandoObject() as IDictionary<string, object>;
        foreach(var pr in t.GetProperties())
        {
            var val = pr.GetValue(fixMe);
            if(val is string && string.IsNullOrWhiteSpace(val.ToString()))
            {
            }
            else if(val == null)
            {
            }
            else
            {
                returnClass.Add(pr.Name, val);
            }
        }
        return returnClass;
    }
}

public class TestClass
{
    public string Name { get; set; }
    public int ID { get; set; }
    public DateTime? DateTime { get; set; }
    public string Address { get; set; }
}

1
所以我必须添加引用吗? - Dhaval Patel
不需要,只需删除.Dump()行,您就可以使用ret将其放入无模式DB中。但是,如果您的DB基于类名,则此方法将无法工作,因为它返回完全不同的类。 - NoLifeKing
1
请注意,固定对象不再是 TestClass 的实例,因此您不能从任何返回 TestClass 的方法中返回它。 - Ilmo Euro
.Dump() 就像我所说的,在 LINQPad 中是一个扩展,主要用于调试,几乎就像 Console.WriteLine 一样。 - NoLifeKing
然后它应该能工作,但我认为你应该听Jon Skeet的话。我的解决方案很不专业。 - NoLifeKing
显示剩余5条评论

6

没有从单个对象中删除属性的概念。属性存在取决于类型,而不是单个对象。

特别地,拥有以下方法始终是有效的:

public void ShowDateTime(TestClass t)
{
    Console.WriteLine(t.DateTme);
}

那段代码无法知道您是否想要从t所引用的对象中“删除”DateTime属性。如果该值为null,则只会获取该值-这很好。但是您不能删除属性本身。
如果您在某个地方“列出”对象的属性,则应在那里进行过滤。
编辑:好的,现在您已经给了我们一些上下文:
好的,我正在使用Schemaless数据库,因此空值和空值也会在数据库中存储空格,这就是原因。
因此,在填充该数据库的代码中,不要设置任何对应于具有null值的属性的字段。这纯粹是数据库填充问题,而不是对象本身的问题。
(我还会争辩说,您应该考虑通过这样做节省多少空间。您真的那么在意吗?)

是的,因为每天我的数据库中会插入250,000条记录,我需要检索并对其进行一些计算,所以我必须关注这个问题。 - Dhaval Patel
3
你是否实际测量过这对数据和检索时间造成的影响?我怀疑它微乎其微。 - Jon Skeet
在我的数据库中,有一个触发器,当另一个表的数据达到 85,00,000 的上限时,可以将其移动。因此,在表中超过 30 天后,大约会有 60,00,00 条数据,并且我正在使用需要加载数据时间的 Silverlight 应用程序。 - Dhaval Patel
850万个什么?字节?行?兆字节?无论如何,你仍然没有回答我的问题,即你是否已经测量了差异。我强烈怀疑你是过早地进行了优化,除非大部分字段在大部分行中都缺失。 - Jon Skeet
850万行。是的,我已经测量过了。 - Dhaval Patel

3
以下是接受答案的“稍微”更清晰和更短的版本。
        /// <returns>A dynamic object with only the filled properties of an object</returns>
        public static object ConvertToObjectWithoutPropertiesWithNullValues<T>(this T objectToTransform)
        {
            var type = objectToTransform.GetType();
            var returnClass = new ExpandoObject() as IDictionary<string, object>;
            foreach (var propertyInfo in type.GetProperties())
            {
                var value = propertyInfo.GetValue(objectToTransform);
                var valueIsNotAString = !(value is string && !string.IsNullOrWhiteSpace(value.ToString()));
                if (valueIsNotAString && value != null)
                {
                    returnClass.Add(propertyInfo.Name, value);
                }
            }
            return returnClass;
        }

最佳答案。但是您可以删除valueIsNotAString,因为还会有其他类型的对象。我们可以将此条件作为布尔值放入参数中进行检查,使其更通用。 - vibs2006

2
您可以利用 动态类型 进行操作:
class Program
{
    static void Main(string[] args)
    {
        List<dynamic> list = new List<dynamic>();
        dynamic
            t1 = new ExpandoObject(),
            t2 = new ExpandoObject();

        t1.Address = "address1";
        t1.ID = 132;

        t2.Address = "address2";
        t2.ID = 133;
        t2.Name = "someName";
        t2.DateTime = DateTime.Now;

        list.AddRange(new[] { t1, t2 });

        // later in your code
        list.Select((obj, index) =>
            new { index, obj }).ToList().ForEach(item =>
        {
            Console.WriteLine("Object #{0}", item.index);
            ((IDictionary<string, object>)item.obj).ToList()
                .ForEach(i =>
                {
                    Console.WriteLine("Property: {0} Value: {1}",
                        i.Key, i.Value);
                });
            Console.WriteLine();
        });

        // or maybe generate JSON
        var s = JsonSerializer.Create();
        var sb=new StringBuilder();
        var w=new StringWriter(sb);
        var items = list.Select(item =>
        {
            sb.Clear();
            s.Serialize(w, item);
            return sb.ToString();
        });

        items.ToList().ForEach(json =>
        {
            Console.WriteLine(json);
        });
    }
}

1

也许接口会很方便:

public interface IAdressAndId
    {
        int ID { get; set; }
        string Address { get; set; }
    }
    public interface INameAndDate
    {
        string Name { get; set; }
        DateTime? DateTime { get; set; }
    }
    public class TestClass : IAdressAndId, INameAndDate
{
    public string Name { get; set; }
    public int ID { get; set; }
    public DateTime? DateTime { get; set; }
    public string Address { get; set; }
}

创建对象:

IAdressAndId t = new TestClass()
            {
                Address = "address",
                ID = 132,
                Name = string.Empty,
                DateTime = null
            };

您可以将接口放在单独的命名空间中,并将类声明设置为 internal。然后创建一些公共工厂,用于创建您的类的实例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接