我希望在课堂上从字典中返回列表,但允许使用预先编写的比较方法进行自定义排序。在我要转换的原始Java代码中,我在类中使用Google Guava Ordering创建了compare方法,然后有一个称为以下方法的方法传入了一种公共比较器方法,其声明如下:
public List<Word> getWords(Comparator c) { }
我正在尝试在C#中重新创建它,但我不知道怎么做。本质上,在下面的代码中,您可以看到每种类型的排序都有三个版本,此外,我最终为每个返回值创建了两个列表,这似乎有点浪费。
我研究了创建委托,但是有点迷茫,然后想到可以创建一个IComparable,但随后看到了IComparator,然后看到Sort方法采用了Comparator。
有人可以指出以最佳方式将其转换为单一类型的“ GetWords”的方向,从而允许客户调用GetWords来从预先提供的排序集中检索排序列表。
public partial class WordTable
{
private Dictionary<string, Word> words;
public WordTable()
{
//for testing
words = new Dictionary<string, Word>();
words.Add("B", new Word("B", WordTypes.Adjective));
words.Add("A", new Word("A", WordTypes.Noun));
words.Add("D", new Word("D", WordTypes.Verb));
}
public List<Word> GetWords()
{
return words.Values.ToList();
}
public List<Word> GetWordsByName()
{
List<Word> list = words.Values.ToList<Word>();
return list.OrderBy(word => word.Name).ToList();
}
public List<Word> GetWordsByType()
{
List<Word> list = words.Values.ToList<Word>();
return list.OrderBy(word => word.Type).ToList();
}
}
答案 0 :(得分:5)
我认为您正在寻找predicates。
实际上,您需要一组预定义的谓词(一个用于ByName
,一个用于ByType
),然后将此谓词传递到GetWords
函数中。
答案 1 :(得分:2)
您可以使用两种方法。
这与您过去的Java经验紧密相关。
官方方法是使用IComparer<T>
(link)。
类似于Java示例中的Comparator
,这使您可以创建均实现IComparer<Word>
接口的不同排序方法,然后可以动态选择排序方法。
作为一个简单的例子:
public class WordNameComparer : IComparer<Word>
{
public int Compare(Word word1, Word word2)
{
return word1.Name.CompareTo(word2.Name);
}
}
然后您可以这样做:
public List<Word> GetWords(IComparer<Word> comparer)
{
return words.Values.OrderBy(x => x, comparer).ToList();
}
您可以通过以下方式致电:
var table = new WordTable();
List<Word> sortedWords = table.GetWords(new WordNameComparer());
当然,您可以通过传递不同的IComparer<Word>
来更改排序逻辑。
根据经验,由于LINQ具有增强的可读性和较低的实现成本,因此这是一种首选方法。
查看最后两个方法,您应该看到唯一可变的部分是用于对数据进行排序的lambda方法。您当然可以将其可变地转换为方法参数:
public List<Word> GetWordsBy<T>(Func<Word,T> orderByPredicate)
{
return words.Values.OrderBy(orderBy).ToList();
}
由于OrderBy
谓词对所选属性使用通用参数(例如,在字符串字段上排序还是int字段?...),因此您必须使此方法通用,但不需要在调用方法时显式使用泛型参数。例如:
var sortedWordsByName = table.GetWordsBy(w => w.Name);
var sortedWordsByLength = table.GetWordsBy(w => w.Name.Length);
var sortedWordsByType = table.GetWordsBy(w => w.Type);
请注意,如果选择一个类而不是值类型,则您将不得不为该类创建并传递一个IComparer<>
,否则该类本身必须实现IComparable<>
因此可以按照您希望的方式对其进行排序。
您可以引入升序/降序:
public List<Word> GetWordsBy<T>(Func<Word,T> orderByPredicate, bool sortAscending = true)
{
return sortAscending
? words.Values.OrderBy(orderBy).ToList()
? words.Values.OrderByDescending(orderBy).ToList();
}
我正在尝试使用委托,但是避免了调用者不得不滚动自己的lambda语句并使用预定义的语句。
您可以简单地用一些预定义的选项包装您的方法:
public List<Word> GetWordsBy<T>(Func<Word,T> orderByPredicate)
{
return words.Values.OrderBy(orderBy).ToList();
}
public List<Word> GetWordsByName()
{
return GetWordsBy(w => w.Name);
}
这样,您的外部呼叫者不需要的话就不必使用lambda;但是您仍然可以在类中保留可重用代码的好处。
有很多方法可以做到这一点。出于可读性考虑,我更喜欢创建预设方法,但您可以改用一个枚举,然后将其映射到正确的Func。或者,您可以创建一些静态预设lambda,供外部调用者参考。或者...世界就是你的牡蛎:-)
答案 2 :(得分:-1)
我希望这行得通,甚至可以编译。
class WordTable
{
public List<Word> GetWords(IComparer<Word> comparer)
{
return words.Values.OrderBy(x => x, comparer).ToList();
}
}
class WordsByNameAndThenTypeComparer : IComparer<Word>
{
public override int Compare(Word x, Word y)
{
int byName = x.Name.CompareTo(y.Name);
return byName != 0 ? byName : x.Type.CompareTo(y.Type);
}
}
用法:
WordTable wt = new WordTable();
List<Words> words = wt.GetWords(new WordsByNameAndThenTypeComparer());