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

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

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

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

Q&A

解決済

5回答

3066閲覧

動的な計算式を登録して計算を行いたい

cutedog

総合スコア177

C#

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

0グッド

0クリップ

投稿2019/02/12 11:36

編集2019/02/13 08:25

こんばんは。

あらかじめ決められた項目の値を取得して、ユーザが登録した計算方法に従い
計算を行いたいという仕様がでてきたのですが
救いとしては、計算に利用する項目はあらかじめ決まっていて
それらを利用するというものなのですが
それでも、まったく実現方法が思い浮かびません。
そもそも、動的な計算式を登録して使うってことが可能なのでしょうか?

例えば、EXCELであれば、項目値を各セルに固定出力し
登録した計算式をセルにセットして個々のセルの値を利用して計算すればいいかなんて
考えたりできるのですが・・・・

以上、何か良いアイデアやヒント、このような仕様のご経験のある方
ご教授よろしくお願いいたします。

<<情報提供頂いた方々へ>>
とりあえず今回の情報で動的な計算ができるとわかったことは大変良かったです。
実装はまだ先なのでそれまでに勉強しておきたいと思います。
ありがとうございました。

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

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

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

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

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

Zuishin

2019/02/13 02:29 編集

「ユーザーが登録」「項目は決まっていて」などがあいまいで、どのようなデータをどのように計算するのか、どこまでの制限があるのかがわかりません。 たとえば四則計算だけでいいのか、それとも分岐が必要なのか、変数やレジスタは必要か、演算子の優先順位も必要か、与えるデータの個数は可変か、など(など、ですよ。これだけではなく、など、です)の細かい条件がわからないことになります。 ということはつまり最大限の自由度を確保しなければならないことになり、実現方法も難解なものを使わざるを得なくなります。 一般に、制限があればあるほど簡単な方法が使えますので、具体的な仕様を明らかにする方が解決が早いと思います。
Zuishin

2019/02/13 02:47

場合によってはそのアプリにデータ取得・設定の API を実装し、ユーザーが外部から PowerShell や Python などで計算するのが柔軟で簡単ということもありますし、あるいはアドインを組み込めるように作る、ユーザーにテキストを入力させてそれを解析するなどの方法もありますが、どれが最適なのかは場合によって違います。
cutedog

2019/02/13 03:10

ありがとうございます。 まず私の知りたいことは、動的な計算というのが可能な否か。 今のところ可能なら、どういった方法があるのかを知りたいくらいの情報収集の段階です。 Zuishinさんが言っておられるほど難しいことはしないです。 具体的な仕様ということにならないですが、やりたいことは 給料計算を行うための計算式が業界内の団体によって違う箇所があるらしく そこを補正するために動的な計算をしたいのです。
Zuishin

2019/02/13 03:43

違うと言っても、そう何十種類もあるわけではないでしょう。 数種類程度なら、あらかじめその計算式をアプリに組み込んでおき、オプションで切り替えるのが良いと思います。 計算式の記述をユーザーに自由にやらせると、後々余計なトラブルが舞い込んで不要な手間がかかりますので、開発者側で組み込んでおくのが一番楽です。 デリゲートの使い方はおわかりですか?
cutedog

2019/02/13 05:29

そうですね。みなさんに頂いた情報を見て検討していたのですが、ちょっと実装工数を考えても費用対効果が薄いように思うので、実際に実装するとなるとZuishinさんの言う通りパターン化したものを切り替えるようにする可能性が大きいです。まぁ最悪は団体毎のカスタマイズ案件になるかもです。 デリゲートはイベントの追加とかで真似して使ったことがありますが、自分でそれ自体を作成したことはないです。作れといわれてもデリゲートの事を理解していないので無理ですね。。。 デリゲートを利用して、パターン化した計算式を組込むみたいな感じですか?
Zuishin

2019/02/13 05:30

サンプルを回答しておきました。
guest

回答5

0

式木で頑張るとか、
https://qiita.com/elipmoc101/items/3d76457394815af98bb1

evalをどっかから使えるようにするとか
https://dobon.net/vb/dotnet/programing/eval.html

投稿2019/02/12 11:45

kiichi54321

総合スコア1986

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

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

cutedog

2019/02/12 12:06

ありがとうございます。 式木というのは初めて知りました。 evalもそうですが、ちょっとハードル高そうですね・・・ DataTable.Computeメソッドが使えそうな感じもしますし 実現が可能か考えてみます。
cutedog

2019/02/13 02:48

おお、しゅごい。。
guest

0

既に回答が出ていますが、どの程度の式を想定しているのでしょうか?
四則演算のみで、変数が数個程度、それとも、Excelで使えるような複雑な計算式まで? まず、サポートの範囲を明確にすることから、始めてはどうでしょう。

はっきり言って、Excelで使えるような複雑な計算式は片手間で難しいと思います。紹介のあったようなライブラリを使うのも一手でしょうが、理解するのも大変。
変数が数個なら、変数は、A, B, C, X, Y, Z だけとか、仕様を絞って実装するのもありです。計算も四則演算+αしかサポートしませんとか、、。
あと、入力をどうするかですね。複雑な式だと構文解析が必要。計算の優先順位も考慮する必要もあります。

まずは、どの程度の範囲でサポートするのか検討が必要と思います。

投稿2019/02/12 14:56

pepperleaf

総合スコア6385

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

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

cutedog

2019/02/12 16:40

まだ計算式は顧客からでていませんが、ある程度、固定な計算式になるち思います。
guest

0

C#スクリプト実行
自分は使ったことがありませんが、C#にはスクリプト機能があります
なのでこの機能を利用して計算を行うようにしてみたらどうでしょうか?
詳細は以下のキーワードで検索するといろいろ出てきますのでご確認ください

  • Microsoft.CodeAnalysis.CSharp.Scripting
  • C# スクリプト
  • Roslyn for Scripting

投稿2019/02/12 12:05

len_souko

総合スコア1367

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

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

cutedog

2019/02/12 12:14

ありがとうございます。 Roslyn for Scriptingがなかなか面白そうな感じですね。。 実現が可能か考えてみます。
len_souko

2019/02/12 12:21

利用者がC#の熟練者だったら直接コードを書かせりゃえぇんとなりますが、そもそもそのレベルならツールを自作するだろうという予想から、画面操作に対してスクリプトコードは自動生成させることになるかと思います
guest

0

ベストアンサー

新しい Windows Forms アプリを作り、Form1.cs を次のように書き換えて実行してみてください。
コンボボックスから給与の計算方法を選び、TAB を押すなどして別のコントロールに移ると給与の計算方法が変わります。

計算方法の種類が限られているなら、このようにあらかじめ登録された計算方法をデリゲートにして切り替えるのが一番トラブルが起きにくいのではないかと思います。

計算方法が新しく追加されるようならコンパイルし直さなければなりませんが、その場合は手数料が取れるのではないかと思いますし、保守は容易です。場合によってはこれに加えてアドインを検討してもいいかと思います。

C#

1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Runtime.CompilerServices; 5using System.Windows.Forms; 6 7namespace WindowsFormsApp1 8{ 9 public partial class Form1 : Form 10 { 11 public Form1() 12 { 13 InitializeComponent(); 14 var people = new BindingList<Person> 15 { 16 new Person() 17 { 18 Name = "Alice", 19 Age = 20, 20 Sex = Sex.Femail 21 }, 22 new Person() 23 { 24 Name = "Bob", 25 Age = 30, 26 Sex = Sex.Male 27 }, 28 new Person() 29 { 30 Name = "Charley", 31 Age = 40, 32 Sex = Sex.Other 33 } 34 }; 35 foreach (var person in people) 36 { 37 person.SalarySystem = SalarySystems[0]; 38 SalarySystemChanged += (sender, e) => 39 { 40 person.SalarySystem = e.System; 41 }; 42 } 43 var dataGridView = new DataGridView() 44 { 45 Parent = this, 46 Dock = DockStyle.Fill, 47 DataSource = people 48 }; 49 var panel = new Panel() 50 { 51 Parent = this, 52 Dock = DockStyle.Top, 53 Height = 25 54 }; 55 var comboBox = new ComboBox() 56 { 57 Parent = panel, 58 Top = 2, 59 Left = 2, 60 Anchor = AnchorStyles.Left, 61 DataSource = SalarySystems, 62 }; 63 comboBox.DataBindings.Add(new Binding(nameof(ComboBox.SelectedItem), this, nameof(SalarySystem), false, DataSourceUpdateMode.OnPropertyChanged)); 64 } 65 66 private SalarySystem salarySystem; 67 public SalarySystem SalarySystem 68 { 69 get => salarySystem; 70 set 71 { 72 if (salarySystem == value) return; 73 salarySystem = value; 74 SalarySystemChanged?.Invoke(this, new SalarySystemEventArgs(value)); 75 } 76 } 77 78 public event SalarySystemChangedEventHandler SalarySystemChanged; 79 80 public IList<SalarySystem> SalarySystems { get; } = new BindingList<SalarySystem>() 81 { 82 new SalarySystem("年功序列", p => p.Age * 10000), 83 new SalarySystem("女性上位", p => p.Sex == Sex.Femail ? 500000 : 50000), 84 new SalarySystem("一律", _ => 100000) 85 }; 86 } 87 88 public class SalarySystem 89 { 90 public SalarySystem(string name, Func<Person, int> calculator) 91 { 92 Name = name; 93 Calculator = calculator; 94 } 95 public string Name { get; private set; } 96 public Func<Person, int> Calculator { get; private set; } 97 public override string ToString() 98 { 99 return Name; 100 } 101 } 102 103 public class SalarySystemEventArgs : EventArgs 104 { 105 public SalarySystemEventArgs(SalarySystem system) 106 { 107 System = system; 108 } 109 public SalarySystem System { get; set; } 110 } 111 112 public delegate void SalarySystemChangedEventHandler(object sender, SalarySystemEventArgs e); 113 114 public enum Sex 115 { 116 Unknown, 117 Male, 118 Femail, 119 Other 120 } 121 122 public class Person : INotifyPropertyChanged 123 { 124 public event PropertyChangedEventHandler PropertyChanged; 125 126 protected void OnPropertyChanged([CallerMemberName]string propertyName = null) 127 { 128 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 129 } 130 131 protected bool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null) 132 { 133 if (EqualityComparer<T>.Default.Equals(field, value)) return false; 134 field = value; 135 OnPropertyChanged(propertyName); 136 OnPropertyChanged(nameof(Salary)); 137 return true; 138 } 139 140 private string name; 141 public string Name 142 { 143 get => name; 144 set => SetProperty(ref name, value); 145 } 146 147 private int age; 148 public int Age 149 { 150 get => age; 151 set => SetProperty(ref age, value); 152 } 153 154 private Sex sex; 155 public Sex Sex 156 { 157 get => sex; 158 set => SetProperty(ref sex, value); 159 } 160 161 private SalarySystem salarySystem; 162 [Browsable(false)] 163 public SalarySystem SalarySystem 164 { 165 get => salarySystem; 166 set => SetProperty(ref salarySystem, value); 167 } 168 169 public int Salary 170 { 171 get => SalarySystem.Calculator(this); 172 } 173 } 174} 175

投稿2019/02/13 05:11

編集2019/02/13 05:17
Zuishin

総合スコア28673

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

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

Zuishin

2019/02/13 06:17

ここでは SalarySystem を Person のインスタンスプロパティにしていますが、静的プロパティにする方がForm1 の SalarySystem プロパティも SalarySystemChanged イベントも無くせるのでいいかもしれませんね。
cutedog

2019/02/13 08:21

サンプルまで頂いてありがとうございます。 コーディングが私から見たら異次元ですわ。 でも、なんとか理解して参考にしてみます。。 ※セッターゲッターのところの文法で=>の部分が私の環境ではエラーとなったので get{retrun xxxx}、set{}に変更して動かしました。
cutedog

2019/02/13 08:31

set,getの=>は C# 7.0からラムダ式で記述できるようなっているんですね。 知らなかったですわ。
guest

0

こちらが参考になりませんか?

投稿2019/02/12 11:43

YAmaGNZ

総合スコア10544

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

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

cutedog

2019/02/12 12:06

ありがとうございます。 evalは難しそうですねー。。 でもDataTable.Computeメソッドが使えそうな感じもしますね。 実現が可能か考えてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問