質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

LINQ

LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

Q&A

解決済

5回答

6356閲覧

C#のLinqを使い独自クラスに実装したIDに重複が無いかどうかを調べたい

necos

総合スコア52

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

LINQ

LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

0グッド

0クリップ

投稿2018/02/26 07:24

編集2018/02/26 11:07

実現したいこと

C#のLinqを使い、以下のようなことをしたいです

  • 実装時に、IDの重複が無いかどうかを調べたい
  • 重複が無ければ、何もしない
  • 重複があれば、そのIDと該当するクラス名をLogに出力したい

解決方法

var duplicationCustomers = CreateCustomers().GroupBy(x => x.Id).Where(x => x.Count() > 1).SelectMany(x => x); foreach (var customer in duplicationCustomers ) { Debug.LogFormat("{0}:{1}", customer.Id, customer.GetType().Name); }

前提条件

こういったクラスを多数作った際にIdの重複が無いかをチェックしたいです。

class Program { interface ICustomer { string Id { get; } void Action(); } public class CustomerA : ICustomer { public string Id => "A"; public voic ACtion() { //個別処理 } } public class CustomerB : ICustomer { public string Id => "B"; public voic ACtion() { //個別処理 } } public class CustomerC : ICustomer { public string Id => "C"; public voic ACtion() { //個別処理 } } static void Main(string[] args) { string id = GetId(); ICustomer _customer = CreateCustomer(id) if(_customer != null) _customer.Action(); } static string GetId() { return ("サーバーから取得したID"); } static ICustomer CreateCustomer(string id) { //swich(id) //{ // case id = "A": // return new CustomerA(); // break; // case id = "B": // return new CustomerB(); // break; // case id = "C": // return new CustomerC(); // break; // ・ // ・ // ・ // 各CustomerクラスにIDを紐付け、Customerクラスが増えたときに、 // Caseが増えるのを避けたい // case id = "D": // return new CustomerD(); // break; // default: // //エラー処理 // break; //} //そのため、switchCaseを使わずに、以下のような実装を検討している return CreateCustomers().FirstOrDefault(x => x.id == id); } }

Reflectionの機能を用いた以下のようなメソッドにより、ICustomerを継承しているクラスの一覧を取得した後に

private static IEnumerable<ICustomer> CreateCustomers() { return Assembly.GetExecutingAssembly().GetTypes() .Where(x => x.IsClass && x.BaseType == typeof(ICustomer)) .Select(x => (ICustomer)System.Activator.CreateInstance(x)); }

Linqを使ってうまく書きたいのですが、うまい書き方があれば教えてください。
よろしくお願いいたします。

環境

C# 4.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2018/02/26 07:49

クラスにIDが紐付いているならstaticかattributeにしたほうがよくて、インスタンスに紐付けるならICustomerにプロパティを実装すればいいと思うのですが、これなにがしたいんですか?
Zuishin

2018/02/26 08:36

ICustomer は名前だけ見るとインターフェースのようですが、BaseType でインターフェースは取得できません。クラスであれば I の頭文字は誤解を招くので止めましょう。
Zuishin

2018/02/26 08:44 編集

またクラスに定数の ID を付与するのであれば、重複のあるなしは設計時にチェックするものであって実行時ではないはずです。重複しない ID のつけ方を調べるのが良いのではないでしょうか。今はどうやってつけていますか?
necos

2018/02/26 08:51

ozwkさん 本来やりたいことを追記しました。
necos

2018/02/26 08:55

Zuishinさん インターフェースの指摘の件、ありがとうございます。修正しておきます。
necos

2018/02/26 08:58

ID付与方法の件は、設計時にチェックしています。今回は、実装時の転記ミス等による重複チェックを目的としてます。
guest

回答5

0

おそらく Action() がそれぞれ違うのでクラスを大量に作っているのではないかと思いますが、これはインスタンスで解決すべき案件だと思います。
以下のように Customer のそれぞれのインスタンスにそれぞれの Action を指定することができます。

C#

1interface ICustomer 2{ 3 string ID { get; } 4 void Action(); 5} 6 7class Customer : ICustomer 8{ 9 public string ID { get; } 10 11 public Customer(string id, Action<Customer> action) 12 { 13 ID = id; 14 this.action = action; 15 } 16 17 Action<Customer> action; 18 public void Action() 19 { 20 if (action != null) action(this); 21 } 22} 23 24var customer1 = new Customer("customer1", customer => Console.WriteLine($"私の ID は {customer.ID} です。")); 25customer1.Action(); 26

重複のチェックですが、設計時であるならばわざわざ Customer を使うまでもありません。
使用する ID のリストがあるはずなので、それを使ってください。
例えば次のように Customer を作っている場合

C#

1List<string> Ids = new List<string>() 2{ 3 "customer1", "customer2", "customer3" 4}; 5 6var customer1 = new Customer(Ids[0], customer => 7{ 8 Console.WriteLine("私は customer1"); 9}); 10var customer2 = new Customer(Ids[1], customer => 11{ 12 Console.WriteLine("私は customer2"); 13}); 14var customer3 = new Customer(Ids[2], customer => 15{ 16 Console.WriteLine("私は customer3"); 17 18});

次のようにチェックできます。

C#

1Console.WriteLine(Ids.Distinct().Count() == Ids.Count() ? "重複はありません" : "重複がありました");

投稿2018/02/26 09:07

編集2018/02/26 09:31
Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

necos

2018/02/26 11:27

丁寧な回答、ありがとうございました。 私の説明不足もあったようで、質問の趣旨と少し離れてしまった様に感じましたので、 ベストアンサーは他の方の回答とさせていただきました。 しかし、内容としては大変参考になりました。 ありがとうございます。
Zuishin

2018/02/26 11:38

見る人の頭にはてなマークの浮かびそうな(実際に浮かんだ)クラスベースの言語らしくない設計を見直すべきと思ったので、趣旨と離れているのは承知の上でより好ましい実装を書きました。 おそらく JavaScript 流でしょうか。 質問者さんの好みに合わない回答は、質問者向けではなくこの質問を見て参考にするほかの人向けだと思ってください。そのような回答をすることはよくあります。
necos

2018/02/26 12:41

補足回答ありがとうございます。 一応、わたしの質問の趣旨は伝わっていたということで、少し安心しました。 そもそも今回の実装の背景には 1 処理をCustmer毎に、とにかくファイルを分けたい 2 実際はActionが10種類程度存在する 3 送られてくるIDをキーとして、処理を動的に振り分けたい といったものがあります。 そのためには、IDと個別の処理(==class)をなんらかの形で紐付ける必要があり、かつCustomerは定期的に増えて行くので、手を加える箇所を極力少なくする意図で、このような形をとりました。
guest

0

ベストアンサー

C# では原則としてクラスのフィールドの公開はしないことになってます。
C# ではマーカーインターフェイスが推奨されませんのでインターフェイスには少なくとも一つのプロパティかメソッドが必要です。
よってこんな感じになるはずです。

C#

1class Program 2{ 3 static void Main(string[] args) 4 { 5 var duplicationCustomers = CreateCustomers() 6 .GroupBy(c => c.Id) 7 .Where(g => g.Count() > 1) 8 .SelectMany(g => g); 9 } 10 11 private static IEnumerable<ICustomer> CreateCustomers() 12 { 13 return Assembly.GetExecutingAssembly().GetTypes() 14 .Where(x => x.IsClass && x.BaseType == typeof(ICustomer)) 15 .Select(x => (ICustomer)System.Activator.CreateInstance(x)); 16 } 17 18 interface ICustomer 19 { 20 string Id { get; } 21 } 22 23 class CustomerA : ICustomer 24 { 25 public string Id => "A"; 26 } 27 28 class CustomerB : ICustomer 29 { 30 public string Id => "B"; 31 } 32 33 class CustomerC : ICustomer 34 { 35 public string Id => "C"; 36 } 37}

Logはお使いの実装で

投稿2018/02/26 07:57

hihijiji

総合スコア4150

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

necos

2018/02/26 11:09

回答ありがとうございます。 ご提示いただきました内容で、やりたいことが実現できましたので、 hihijijiさんの回答を今回のベストアンサーといたしました。
guest

0

私も、KSwordOfHasteさんと同じく重複チェックにはGroupByとAny(Count() > 1)を使います。

C#

1// クラスの定義 2public class ICustomer 3{ 4 string id = "Base"; 5 public virtual string ID => id; 6} 7 8public class CustomerA : ICustomer 9{ 10 string id = "A"; 11 public override string ID => id; 12} 13 14public class CustomerB : ICustomer 15{ 16 string id = "B"; 17 public override string ID => id; 18} 19 20public class CustomerC : ICustomer 21{ 22 string id = "C"; 23 public override string ID => id; 24}

C#

1// 判定部分 2private static bool IsDuplicate() 3 => Assembly.GetExecutingAssembly().GetTypes() 4 .Where(x => x.IsClass && x.BaseType == typeof(ICustomer)) 5 .Select(x => (ICustomer)System.Activator.CreateInstance(x)) 6 .GroupBy(x => x.ID) 7 .Any(x => x.Count() > 1);

投稿2018/02/26 07:47

YamakawaJunichi

総合スコア630

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

necos

2018/02/26 11:28

回答ありがとうございます。 GroupByとAny(Count() > 1)で、重複チェックができるのですね。 ありがとうございました。
guest

0

GroupBy
https://msdn.microsoft.com/ja-jp/library/bb534304(v=vs.110).aspx

を使ってみたらいいんじゃないでしょうか?同じキーを持つ要素があるかどうかはIGroupingインターフェースのCountが1以上かどうかでチェックできますし。

投稿2018/02/26 07:31

編集2018/02/26 07:32
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

necos

2018/02/26 07:51 編集

回答、ならびにURLの添付、ありがとうございます。 GroupByで、Countをみれば、重複しているかどうかがわかる ということですね! 試してみます。
guest

0

解決済みではありますが、ちょっとだけ。
switch や FirstOrDefault が文中にありますが、このような場合は Dictionary を使うべきです。
Dictionary であればキーの一意性も確認できますし、毎回の検索が早くなります。

csharp

1public class Strategy 2{ 3 public Strategy() 4 { 5 var customers = this 6 .GetType() 7 .Assembly 8 .GetTypes() 9 .Where(x => x.IsClass && !x.IsAbstract && typeof(ICustomer).IsAssignableFrom(x)) 10 .Select(x => Activator.CreateInstance(x) as ICustomer); 11#if 例外版 12 // 重複時 ArgumentException 13 _table = customers.ToDictionary(x => x.ID); 14#else // ログ版 15 var dict = new Dictionary<string, ICustomer>(); 16 foreach (var c in customers) 17 { 18 if (!dict.ContainsKey(c.ID)) 19 { 20 dict.Add(c.ID, c); 21 } 22 else 23 { 24 // ログ 25 } 26 } 27 _table = dict; 28#endif 29 } 30 31 private readonly Dictionary<string, ICustomer> _table; 32 33 public void DoAction(string id) 34 { 35 ICustomer customer; 36 if (_table.TryGetValue(id, out customer)) 37 { 38 customer.Action(); 39 } 40 } 41}

csharp

1public class Progmram 2{ 3 private static _strategy = new Strategy(); 4 public static void Main() 5 { 6 _strategy.DoAction(GetId()); 7 } 8}

投稿2018/04/21 23:51

gaya-K

総合スコア449

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問