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

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

ただいまの
回答率

90.47%

  • C#

    7447questions

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

  • Xamarin

    519questions

    Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

C# プロパティをまとめてコードの可読性を高めたい

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 576

naritamago

score 7

Xamarin勉強中です。

動的に変化する値をデータバインディングでViewに表示させるViewModelを実装していますが、
プロパティが多く、以下のような状況になっています。

        private string _xAccel;
        public string xAccel
        {
            get { return _xAccel; }
            set
            {
                _xAccel = value;
                OnPropertyChanged("xAccel");
            }
        }
        private string _yAccel;
        public string yAccel
        {
            get { return _yAccel; }
            set
            {
                _yAccel = value;
                OnPropertyChanged("yAccel");
            }
        }
        private string _zAccel;
        public string zAccel
        {
            get { return _zAccel; }
            set
            {
                _zAccel = value;
                OnPropertyChanged("zAccel");
            }
        }
        private string _xHoge;
        public string xHoge
        {
            get { return _xHoge; }
            set
            {
                _xHoge = value;
                OnPropertyChanged("xHoge");
            }
        }
/////////////////////中略/////////////////////


ViewModelはINotifyPropertyChangedの実装を提供する基本クラスを継承しています。
Accelは加速度センサーの事なんですが、これがジャイロ・コンパス・GPS・etc…と増えて行くとプロパティだけでコードが肥大化してしまうのでセンサー単位で纏めたいのですが、どうすれば良いのでしょうか?

BA選択後追記

今回質問させていただいて、いろいろな実装方法を教えていただきました。MVVMのポリシーの話も議論されておりましたが、一先ずはFodyを使用した方法でVMに記述し、#regionでまとめるといった方法を採択しました。
質問してよかったです。ありがとうございました。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+6

質問は「プロパティをまとめたい」ですが、その後のコメントを見るとプロパティの記述が簡単になりさえすればいいように見えます。
また、プロパティは何か特別な処理をするものではなく、定型的なものが並ぶだけなのでそれをループで処理できれば最も意図に沿うのではないかと思いました。

テキストテンプレートを使うと、いくらプロパティが増えても大丈夫です。
ただし、補完が使えなくなるので、主となるソースとは別ファイルにして partial class とすれば良いと思います。これによって一つのクラスを複数のファイルに分割して記述できます。

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public partial class Class1
    {
<#
var names = new string[]
{
    "xAccel", "yAccel", "zAccel", "xHoge"
};
foreach (var name in names)
{
#>
        private string _<#= name #>;
        public string <#= name #>
        {
            get => _<#= name #>;
            set
            {
                if (value == _<#= name #>) return;
                _<#= name #> = value;
                OnPropertyChanged("<#= name#>");
            }
        }
<#
}
#>
    }
}

テキストテンプレートの記述は上記です。
プロパティが増えた時には names の要素を増やしてください。
これを保存すると次のように展開された cs が自動的に生成されます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public partial class Class1
    {
        private string _xAccel;
        public string xAccel
        {
            get => _xAccel;
            set
            {
                if (value == _xAccel) return;
                _xAccel = value;
                OnPropertyChanged("xAccel");
            }
        }
        private string _yAccel;
        public string yAccel
        {
            get => _yAccel;
            set
            {
                if (value == _yAccel) return;
                _yAccel = value;
                OnPropertyChanged("yAccel");
            }
        }
        private string _zAccel;
        public string zAccel
        {
            get => _zAccel;
            set
            {
                if (value == _zAccel) return;
                _zAccel = value;
                OnPropertyChanged("zAccel");
            }
        }
        private string _xHoge;
        public string xHoge
        {
            get => _xHoge;
            set
            {
                if (value == _xHoge) return;
                _xHoge = value;
                OnPropertyChanged("xHoge");
            }
        }
    }
}

追記

プロパティ名だけでなく型も指定する場合

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public partial class Class1
    {
<#
var properties = new[]
{
    new { Name = "xAccell", Type = "string" },
    new { Name = "yAccell", Type = "string" },
    new { Name = "zAccell", Type = "string" },
    new { Name = "xHoge", Type = "string" },
};
foreach (var property in properties)
{
#>
        private <#= property.Type #> _<#= property.Name #>;
        public <#= property.Type #> <#= property.Name #>
        {
            get => _<#= property.Name #>;
            set
            {
                if (value == _<#= property.Name #>) return;
                _<#= property.Name #> = value;
                OnPropertyChanged("<#= property.Name #>");
            }
        }
<#
}
#>
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

+3

いくつか対策はありますが、実装方法が簡素になるのは次のいずれかでしょう。

前者はReactiveプログラミングをしないのであれば、不要ライブラリが多数導入されますし、オーバースペックになるかもしれません。
後者はILの修正が入る為、黒魔術感がありますが、古くからあって利用者も多いライブラリなので、そこまで忌避するものでもない気がします。

こちらの記事に良くまとまって見るので一読いただいて、いずれを選択するか判断していただくのが良いと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/31 18:57 編集

    nuitsさん、ご回答ありがとうございます。Fody、早速試そうとしてみたのですが「FodyWeavers.xmlが見付からない」とエラーが出たので中断しました。またnuitsさんのブログにもたどり着いたのですが、VisualStudio2017では使えない?というような記述があったのですが…
    一応当方の環境はVisualStudioforMacです。
    あと、今の所Reactiveは使う予定はありません。

    キャンセル

  • 2018/08/31 21:02 編集

    動かないのは、MethodDecorator.Fodyです。PropertyChanged.Fodyは動いていました。ここ数ヶ月触ってませんが。
    Fody系のものを使う場合、プロジェクトルートにFodyWeavers.xmlを追加してあげる必要があります。
    中身はこんな感じ

    <?xml version="1.0" encoding="utf-8" ?>
    <Weavers>
    <PropertyChanged/>
    </Weavers>

    VS4Macでは直接試したことがそう言えば無いので、後で試してまたここに返信しますね。
    遅い時間になると思いますが。

    キャンセル

  • 2018/08/31 23:02

    ろくに調べもしないで適当なことを言ってしまい、失礼しました。
    訳あってVS4MacではなくVS2017なのですが、Fody動かすことができました。
    ```
    public string xAccel { get; set; }
    public string yAccel { get; set; }
    public string zAccel { get; set; }
    ```
    これだけで動くのマジで黒魔術ですね…
    正直めちゃくちゃベストアンサーにしたいんですが、チーム開発のときに説明がめんどくさくなるのと、hihijijiさんのやり方も試してみたいと思いますのでクローズまで少しお待ちください。

    キャンセル

  • 2018/08/31 23:17

    う、動いて良かったですね。
    VS4Macの手順まとめたので、良かったらどうぞ
    http://www.nuits.jp/entry/propertychanged-fody-for-vs4mac

    キャンセル

+1

例えば、
AccelerationSensorsServiceとでも名前を付けたModelのクラスを作って
そのクラスなかで変更通知付きのX,Y,Zのプロパティを公開して
ViewModelではそのインスタンスを公開すればOKでは?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/31 23:21

    この方法は、二つほど問題があると思います。
    1. 変更通知はModelからイベントが発生するが、Viewが監視しているのはVMのイベントなので、実際にはおそらくプロパティが更新されても画面が更新されない
    2. ModelのプロパティをVMのプロパティとしてそのまま公開するのは、ModelがVMによってカプセル化されておらず、設計的にあまり良いとは言えない

    そもそもModelにはVMに書くのが大変だと質問者がおっしゃっていたコードと同じコードを書かないといけないので、この場合は適さないかもしれませんね。

    キャンセル

  • 2018/09/01 10:11

    MVVMの概念を語ると宗教論になってしまいますので概念論は避けます
    1.普通に更新されます。
    2.具体的にどんな弊害がありますか?

    キャンセル

  • 2018/09/01 10:14

    カプセル化などする必要ありません。View は Model に依存して大丈夫です。
    VM のプロパティのプロパティをバインドすればいいので 1 についても問題ありません。

    キャンセル

  • 2018/09/02 23:28

    hihijijiさん
    MVVMに触れない範囲で回答させていただきます。

    まず、AccelerationSensorsServiceのプロパティをVMのプロパティとして公開する、という意味と取り違えていました。失礼しました。
    であれば更新されますね。

    ただその方法はAccelerationSensorsServiceに、INotifyPropertyChanged絡みのコードが移動しているだけではありませんか?
    ViewModelがシンプルになった代わりにModelが煩雑になって、問題個所が異動しただけで解決に至っていないと思いますが如何でしょうか?

    キャンセル

  • 2018/09/03 11:13

    表題「プロパティをまとめてコードの可読性を高めたい」
    および「センサー単位で纏めたい」に対して
    ・ViewModelをシンプルにして可読性を高める。
    ・Modelの構造化を進めて保守性を上げる。
    の2点を意図した回答です。
    コード量を減らす意図は全くありません。

    キャンセル

  • 2018/09/03 13:14

    それとViewModelの実装如何に関わらず、Modelが不変でない限りModelにINotifyPropertyChangedの実装は必須です。

    キャンセル

  • 2018/09/03 16:04

    AndroidもiOSも加速度はイベントリスナーで受け取るので、Modelではイベントとして処理するイメージでいました。
    確かにイベントで扱われないならおっしゃる通りですね。

    キャンセル

+1

1 #region を使う 
https://msdn.microsoft.com/ja-jp/library/windows/apps/9a1ybwek(v=vs.120)

2 MVVM用のライブラリは、便利機能があるので使う。
https://qiita.com/kiichi54321/items/89f5a6d265710b51aa0d 
コードスニペットを組み合わせるといい。

public string property{get{return _property ;}set{Set(ref _property ,value);}}
private string _property = default(string);


これくらいの記述量で済む

3 センサー周りで固めたいのなら、それ用のVMをつくる。
別に、VMの中にVMを置いたっていい。

public class メインVM
{
  public センサーVM センサーVM{get;set;} = new センサーVM();
}

サービスクラスを作って、DIするのがクールかな。

4 プロパティをクラスにする。

public class プロパティ<T>: INotifyPropertyChanged
{
  private T _value = defult(T);
  public T Value {
定番のINotifyPropertyChangedの処理
  }
}


使い方

public class メインVM
{
  public プロパティ<int> プロパティ名{get;} = new プロパティ<int>();
}


まぁ、ゼロ機能なリアクティブプロパティと同じなんですけど。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

関連した質問

同じタグがついた質問を見る

  • C#

    7447questions

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

  • Xamarin

    519questions

    Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。