【问题标题】:Is is possible to add a calculative property based on the sibling class property C#?是否可以基于同级类属性 C# 添加计算属性?
【发布时间】:2020-10-13 03:08:22
【问题描述】:

我有一个类型的类:

public class sample
{
    public int id;
    public string s;
    //Need a calculative property based on sibling value
    //Ex. public float CharRatio
}

考虑以下数据集

List<sample> s = new List<sample>();
s.Add(new sample{id = 1, s = "source"}); //id 1 is always source charRatio 100 since 6/6 in percentage
s.Add(new sample{id = 2, s = "test"}); //charRatio = 66
s.Add(new sample{id = 3, s = "test"}); //charRatio = 66
s.Add(new sample{id = 4, s = "test"}); //charRatio = 66

是否可以向类添加一个计算属性,该属性将使用 LINQ/LAMBDA 或任何解决方法根据集合中兄弟的值计算值。 我正在尝试找到一种方法来添加使用以下方法计算的 charRatio:

charRatio = source.Len/test.Len

源使用 ID 标识,该 ID 始终为 1。 我的要求需要它是一个计算属性,以便我可以在表达式树中使用它。

【问题讨论】:

  • 明确一点,你基本上想要1/ where(s == "test").Count?还是“源”的长度/“测试”的长度
  • @janzen 我已经用所需的 cmets 编辑了问题!我基本上想通过查找源字符串长度并将其除以所有字符串长度来计算 charRatio,我希望 cmets 清楚
  • @Comraid 你可以查看我的答案

标签: c# oop


【解决方案1】:

这是一个属性,其值是根据给定的属性/值计算的。我希望我能正确理解您的要求。让我知道它是否不符合您的需要。我也尝试写尽可能多的 cmets。所以让代码来说话吧:)

public class Sample {

    /// <summary>
    /// This sourcestring is set to the same value of Sample with Id = 1
    /// </summary>
    private string _sourceStringOfSampleWithId1;

    /// <summary>
    /// Creates a Sample instance
    /// </summary>
    /// <param name="id">Id of Sample</param>
    /// <param name="source">Source of Sample</param>
    public Sample(int id, string source) {
        Id = id;
        Source = Source;
    }

    /// <summary>
    /// Id of Sample
    /// </summary>
    public int Id { get; }
    
    /// <summary>
    /// Source of Sample
    /// </summary>
    public string Source { get; }
    
    /// <summary>
    /// Calculative property for CharRatio
    /// </summary>
    public double CharRatio => (double)_sourceStringOfSampleWithId1.Length / Source.Length * 100;

    /// <summary>
    /// Sets SourceString of Sample with Id = 1
    /// </summary>
    /// <param name="sourceString"></param>
    public void SetSourceString(string sourceString) {
        _sourceStringOfSampleWithId1 = sourceString;
    }

}

这是你如何使用它:

List<Sample> sampleList = new List<Sample>(); // Creates a sample list
sampleList.Add(new Sample(1, "source")); //id 1 is always source charRatio 100 since 6/6 in percentage
sampleList.Add(new Sample(2, "test")); //charRatio = 66
sampleList.Add(new Sample(3, "test")); //charRatio = 66
sampleList.Add(new Sample(4, "test")); //charRatio = 66
var sampleWithId1 = sampleList.Find(s=>s.Id == 1); // Find sample with Id = 1
sampleList.ForEach(s=>s.SetSourceString(sampleWithId1.Source)); // Set all samples calcualtive source string to Sample with Id =1
var ratio = sampleList[2].CharRatio; //66.66; // Get an example from list and see char ratio

基于 cmets,这里是 Sample 类的编辑版本。我也保留原来的答案。因此,请使用适合您的用例的任何一个。我不会评论这种情况,因为从原始答案中应该很明显。

public class SampleWithStaticField {

    private static string _sourceStringOfSampleWithId1;
    public SampleWithStaticField(int id, string source) {
       Id = id;
       Source = Source;
       // Set static source string which will be shared between all instances
        if(Id == 1) {
            _sourceStringOfSampleWithId1 = source;
       }
    }
    public int Id { get; }
    public string Source { get; }
    public double CharRatio {
        get {
            if(_sourceStringOfSampleWithId1 == null) {
                throw new Exception($"{nameof(_sourceStringOfSampleWithId1)} is not set!");
            }
            return (double)_sourceStringOfSampleWithId1.Length / Source.Length * 100;
        }
    }
}

这里是你如何使用它

var sampleList = new List<Sample>();
sampleList.Add(new Sample(1, "source")); //id 1 is always source charRatio 100 since 6/6 in percentage
sampleList.Add(new Sample(2, "test")); //charRatio = 66
sampleList.Add(new Sample(3, "test")); //charRatio = 66
sampleList.Add(new Sample(4, "test")); //charRatio = 66
var ratio = sampleList[2].CharRatio; //66.66;

【讨论】:

  • 感谢您的快速回复!我将不得不在另一个班级中使用它!因为示例类与我的主类是一对多的关系!您认为在设置值时使用循环是正确的吗?!有没有办法在集合中拥有一个共享变量?可以在设置带有源 ID 的字符串时设置吗?这将消除循环的使用,但我不确定这是否可能,但我愿意接受建议
  • @Comraid,你可以选择一个静态变量,所有 Sample 实例都可以访问它。然后每个示例类都可以访问静态值,您不需要使用 foreach
  • 但是静态变量不会是所有集合中的样本吗?我希望在每组集合之间共享一个变量!是否可以限制它?
  • @Comraid,是的,这是正确的。静态变量将在所有Sample 类之间共享。简单地说,如果您有一个标识符,例如每个集合的唯一名称,那么您也可以使用字典
  • @Comraid,如果我是你,我会在List&lt;Sample&gt; 级别添加另一层抽象,例如SampleCollection,你的生活会变得如此轻松
【解决方案2】:

如果列表的id = 1 始终是源的基础并且它是UNIQUE

,我的方法是这样的
public class sample
{
    public int id {get; set;
    public string s {get; set;}
    public float ratio {get; set;}
}


var list = new List<sample>();
//same values you listed
//...
//...

然后我会通过foreach 来添加比率

var sourceObject = list.First(x => x.Id == 1);
var result = list.ForEach(x => {
    
   if (x.Id == sourceObject.Id)
   {
       x.ratio = 1;
   }
   else
   {
       x.ratio = x.s.Length/sourceObject.Length * 100
   }
    
});


【讨论】:

  • 如果可能,总是选择不可变的属性
  • 感谢您的回复,但我有点想找一些没有循环的东西!