Как передать произвольное количество параметров в C # - PullRequest
2 голосов
/ 28 января 2009

ОК. Мне нужно разработать способ, чтобы отслеживать, сколько существует каждого элемента. Есть приблизительно 26 пунктов. Мне также нужен способ узнать, существует ли определенная комбинация предметов.

Например, Это движок для карточной игры. Каждая карта имеет свой тип, и к каждой карте может быть прикреплена карта. Для выполнения определенных действий в игре игрок должен иметь определенную комбинацию карт. Чтобы упростить эту программу, я хотел бы сделать что-то вроде

if (meetsCrit(2, water, 4, ground))
{
    do this()
}
else
{
    displayerror()
}

РЕДАКТИРОВАТЬ: решено!

Я использовал комбинацию техник, описанную в нескольких постах ниже. Специальное упоминание:

Джон Скит, Ринат Абдуллин, Франк,

Во всяком случае, вот что я сделал Я создал класс с именем pair, который хранит искомый тип и номер этого типа. Затем я использовал делегат предиката, чтобы найти все эти типы и посчитать, сколько их, Затем я сравнил его с числом, которое искал, и вернул соответственно true или false.

Это код для него

public bool meetsCrit(params Pair[] specs)
{
    foreach (Pair i in specs)
    {
        if (!(attached.FindAll(delegate(Card c) { return c.type == i.type; }).Count >= i.value))
        {
            return false;
        }

    }
    return true;
}

Ответы [ 13 ]

23 голосов
/ 28 января 2009

Использование params :

Ключевое слово params позволяет указать параметр метода , который принимает аргумент, в котором число аргументов является переменным.

Никакие дополнительные параметры не разрешены после ключевого слова params в объявлении метода, и только одно ключевое слово params разрешено в объявлении метода ...

14 голосов
/ 29 января 2009

Используйте ключевое слово params для передачи переменного количества аргументов:

   private static void HowMayItems<T>(params T[] values) {
        foreach (T val in values) { 
            //Query how many items here..
        }
    }

Также вы можете создать предикат и передать ему фильтр параметров. В методе вы можете вернуть объединение всех результатов. Что-то вроде этого:

public class Item { 
    public string Name { get; set; }
}
public class ItemFilter {
    public string Name { get; set; }
    public ItemFilter(string name) {
        Name = name;
    }
    public bool FilterByName(Item i) {
        return i.Name.Equals(Name);
    }
}

public class ItemsTest {
    private static List<Item> HowMayItems(List<Item> l,params ItemFilter[] values)
    {
        List<Item> results= new List<Item>();
        foreach(ItemFilter f in values){
            Predicate<Item> p = new Predicate<Item>(f.FilterByName);
            List<Item> subList = l.FindAll(p);
            results.Concat(subList);
        }
        return results;
    }
}

EDIT:

ОК, как на счет моей версии со смешанными орехами :)

public enum ItemTypes{
    Balloon,
    Cupcake,
    WaterMelon
    //Add the rest of the 26 items here...
}

public class ItemFilter {
    private ItemTypes Type { get; set; }
    public ItemFilter(ItemTypes type) {
        Type = type;
    }
    public bool FilterByType(ItemTypes type) {
        return this.Type == type;
    }
}

public class PicnicTable {
    private List<ItemTypes> Items;

    public PicnicTable() {
        Items = new List<ItemTypes>();
    }

    public void AddItem(ItemTypes item) {
        Items.Add(item);
    }

    public int HowMayItems(ItemTypes item)
    {
        ItemFilter filter = new ItemFilter(item);
        Predicate<ItemTypes> p = new Predicate<ItemTypes>(filter.FilterByType);
        List<ItemTypes> result = Items.FindAll(p);
        return result.Count;
    }
}

public class ItemsTest {
    public static void main(string[] args) {
        PicnicTable table = new PicnicTable();
        table.AddItem(ItemTypes.Balloon);
        table.AddItem(ItemTypes.Cupcake);
        table.AddItem(ItemTypes.Balloon);
        table.AddItem(ItemTypes.WaterMelon);
        Console.Out.WriteLine("How Many Cupcakes?: {0}", table.HowMayItems(ItemTypes.Cupcake));
    }
}
10 голосов
/ 29 января 2009

Массив params - очевидный ответ. Альтернативой является создание класса для представления критериев. Затем вы можете использовать инициализаторы коллекций, например:

bool result = myCollection.Contains(new Criteria {
                 { 2, Item.Balloon },
                 { 4, Item.Cupcake }
              });

Но если это тоже не полезно, нам определенно понадобится дополнительная информация в вопросе.

Если бы вы могли привести пример синтаксиса, с которым вы бы хотели вызвать метод, это, безусловно, помогло бы нам.

3 голосов
/ 29 января 2009

Ключевое слово params позволяет передавать произвольное количество параметров в форме, отличной от массива. Однако они будут преобразованы в массив внутри функции. Я не знаю ни одной программной конструкции, которая будет отличаться от любой другой языка.

1 голос
/ 29 января 2009

Вот как я бы сделал это в упрощенной форме:

var collection = new[] {Item.Baloon, Item.Cupcake, Item.Baloon, Item.Coke};

var result = collection.Contains(2.Baloons(), 1.Cupcakes());

где:

public enum Item
{
    Baloon,
    Cupcake,
    Coke
}

public static class TableExtensions
{
    public static Pair<Item, int> Baloons(this int count)
    {
        return Tuple.From(Item.Baloon, count);
    }

    public static Pair<Item, int> Cupcakes(this int count)
    {
        return Tuple.From(Item.Cupcake, count);
    }

    public static bool Contains(this IEnumerable<Item> self, 
        params Pair<Item, int>[] criteria)
    {
        foreach (var pair in criteria)
        {
            var search = pair.Key;
            if (self.Count(item => item == search) < pair.Value)
                return false;
        }
        return true;
    }
}

Примечания:

  • Вам не важен порядок пунктов в параметрах.
  • Я использую перечисления для представления объектов, но это могут быть и объекты
  • Кортежи происходят из Lokad.Shared
  • Содержит в настоящее время поиск "по крайней мере X предметов". Мы могли бы иметь большую гибкость , передавая объекты критериев, которые фактически являются предикатами для перечислимых - rules . Это сохранит синтаксис тот же , но позволит иметь больше параметров (например: AtLeast , AtMost , X. VegetarianSets , Y Напитки и т. Д.). Если интересно, ознакомьтесь с предопределенными правилами проверки из блока приложения проверки и бизнес-правил для аналогичного подхода к составлению правил.
1 голос
/ 29 января 2009

ОК, потому что мне показалось, что это какой-то код, чтобы выяснить, сколько у вас экземпляров произвольного числа типов в каком-то списке ...

Мне нужен смешной счетчик:

  public class FunnyCounter
  {
    Dictionary<Type, int> restrictions = new Dictionary<Type, int>();
    public FunnyCounter Add<T>(int appears)
    {
      restrictions.Add(typeof(T), appears);
      return this;
    }

    public void PassThrough(object o)
    {
      if (restrictions.ContainsKey(o.GetType()))
        restrictions[o.GetType()] = restrictions[o.GetType()] - 1;
    }

    public bool SatisfiedAll()
    {
      return restrictions.Values.Aggregate(true, (b, i) => b && i == 0);
    }

Теперь со списком вроде

List<Stuff> l = new List<Stuff> { new Ball(), new Ball(), new Bucket() };

Я делаю ...

FunnyCounter counter = new FunnyCounter();
counter.Add<Ball>(2).Add<Bucket>(1);
l.ForEach(counter.PassThrough);
Console.WriteLine(counter.SatisfiedAll());
0 голосов
/ 29 января 2009

Звучит как хорошая возможность сделать ООП:

скажем, у вас есть несколько классов для представления ваших предметов

public class Item
{
  public string Name{ get; set; }
  // Here put all the properties that are common to all items.
}

public class Cupcake : Item
{
  // By inheriting from Item, it will have Name 
  //  along with anything else you define there

  public bool HasFrosting{ get; set; }
  // Put whatever else Cupcake needs here
}

public class Baloon : Item
{
  public Color MyColor{ get; set; }
}

Тогда в вашем приложении код

// From your post, I can't tell where you're getting
//  your items, so I'll just put a placeholder here for now.
//  I assume this returns all kinds of items:
//  Cupcakes, Baloons, and other fun stuff
Item[] items = GetItemsFromSomewhere();

int cupcakeCount = GetCupcakeCount(items);

...

public int GetCupcakeCount( Item[] items )
{
  int cupcakeCount = 0;

  // This is the code for checking types of items
  //  you could use a LINQ statement or whatever you like
  //  but i just put a foreach loop here for simplicity
  foreach( Item item in items )
  {
    // Check the types of each object, 
    if(item is Cupcake)
     ++cupcakeCount;
  }

  return cupcakeCount;
}

Даже если вы можете сделать это по-другому, рассмотрите ООП, так как кажется, что вам необходимо различать типы объектов и сравнивать их. Это может быть хорошим способом точного представления вашей системы.

0 голосов
/ 29 января 2009

Интересно, вопрос несколько нарушен? Если вы спрашиваете какой-либо объект, содержит ли он n из этого и y из этого, тогда ваш объект может быть перечислен. В этом случае он должен реализовать IEnumerable, где T будет «общим знаменателем вашего содержания». отсюда вы можете использовать, например, LINQ, чтобы получить ответы на ваши вопросы или инкапсулировать такие вопросы в объектах и ​​предоставить, например, система критериев, как показал Джон ...

0 голосов
/ 29 января 2009

Массив, вероятно, сделает эту работу, но он не совсем элегантен. У вас есть класс с некоторыми коллекциями с различными типами предметов? Еще одно более точное решение - использование словаря

Dictionary<String, int> numberOfEach = new Dictionary<string, int>();
numberOfEach.Add("balloons", 2);
numberOfEach.Add("cupcakes", 4);

Тогда у вас будет метод в вашем классе (тот, что с коллекциями)

public bool correctAmmounts(Dictionary<String, int> numberOfEach)
{
    return collection1.Count == numberOfEach["balloons"] &&
           collection2.Count == numberOfEach["cupcakes"];
}

Где collection1 имеет несколько воздушных шаров, а collection2 - несколько кексов.

Вам придется изменить ваш метод, если количество различных типов изменится.

0 голосов
/ 29 января 2009
bool doTheseItemsExist(List criteria) { .. }

Разве вы не можете просто передать список объектов? Технически это похоже на передачу массива, но с ним легко работать, и вам не нужно загружать стек вызовов тем, что может быть потенциально большим списком параметров, если вы используете params. (Эта последняя часть, конечно, полностью зависит от того, по скольким критериям вы собираетесь искать.)

(РЕДАКТИРОВАТЬ: На самом деле, этот список должен быть отображением типа объекта и ожидаемого числа.)

Но господа, которые предложили, чтобы ваша коллекция выставляла эти запросы в вашей коллекции, вероятно, победители здесь. Одна возможность:

bool MyCollection.HasBalloons(int numBalloons);
bool MyCollection.HasCupcakes(int numCupcakes);
bool MyCollection.HasPartyHats(int numHats);

Но тогда результат bool неоднозначен для вызывающей стороны ... точно так много? по крайней мере это много? Так что, возможно, лучшее направление:

int MyCollection.NumBalloons();
int MyCollection.NumCupcakes();
int MyCollection.NumPartyHats();

Тогда вам не понадобится какая-то дополнительная функция с произвольным набором параметров. Вы можете просто проверить коллекцию по мере необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...