20200221のC#に関する記事は12件です。

『Unity C# 個人用メモ』 アイテムの生成一括と一括削除

public class Seisei : MonoBehaviour
{
    //生成したいオブジェクトを宣言
    [SerializeField] GameObject obj = default;

    void Update()
    {
        //一括生成
        if (Input.GetMouseButtonDown(0))
        {
            //x座標用
            for (int xi = 0; xi < 5; xi++)
            {
                //y座標用
                for (int zi = 0; zi < 3; zi++)
                {
                    //生成(対象,座標,角度)
                    Instantiate(obj, new Vector3(xi, 1f, zi), Quaternion.identity);
                }
            }
        }
    //一括削除
    if (Input.GetMouseButtonDown(1))
    {
        //あらかじめオブジェクトにタグをつけておいて、そのタグのついたオブジェクトをまとめて配列にぶち込む
        GameObject[] objs = GameObject.FindGameObjectsWithTag("obj");

        //配列onjsにぶち込んだものをを一纏めにする
        foreach(GameObject clone in objs)
        {
            Destroy(clone);
        }
    }
}

}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『Unity C# 個人用メモ』 アイテムの一括生成と一括削除

public class Seisei : MonoBehaviour
{
    //生成したいオブジェクトを宣言
    [SerializeField] GameObject obj = default;

    void Update()
    {
        //一括生成
        if (Input.GetMouseButtonDown(0))
        {
            //x座標用
            for (int xi = 0; xi < 5; xi++)
            {
                //y座標用
                for (int zi = 0; zi < 3; zi++)
                {
                    //生成(対象,座標,角度)
                    Instantiate(obj, new Vector3(xi, 1f, zi), Quaternion.identity);
                }
            }
        }
    //一括削除
    if (Input.GetMouseButtonDown(1))
    {
        //あらかじめオブジェクトにタグをつけておいて、そのタグのついたオブジェクトをまとめて配列にぶち込む
        GameObject[] objs = GameObject.FindGameObjectsWithTag("obj");

        //配列onjsにぶち込んだものをを一纏めにする
        foreach(GameObject clone in objs)
        {
            Destroy(clone);
        }
    }
}

}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#OpenCV Sharpでメモリリークが起きる

本内容を利用した場合の一切の責任を私は負いません。

Matをusingしてるのに起きた。
(下記の公式サンプルでusingしてるためアンマネージ。
https://github.com/shimat/opencvsharp_samples/
)
検索したら下記が見つかって、自分はMat取得ではなくBitmap取得(Mat.ToBitmapメソッド)だから該当しないと思ったけど、Disposeしたら防止できた。
https://blog.mohyo.net/2015/03/1313/
てことで、Matから取得したアンマネージは全てDisposeした方がよさそう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今更ながらWPFに置き換えてみる(1)

この前新しいノートPC買った。
レノボのX1。すこぶる快適なんですが、以前作ったツールアプリの表示が汚い。解像度の問題で仕方ないのはわかっているんですが何とかしたいと考えてWPFで作り直すことにします。
どうせ作り直すので、VBからC#に置き換えもやってみようかと。


現行のFORMはこんな感じ
コメント 2020-02-21 195453.png

↑超ボケボケ。ひどい。

機能は自分ツールなんであまりなく
・常駐型アプリ
・NotifyIconの動的生成(数種類)
・時報表示
くらい。

いまさらWPFってのもあれですが自分用の備忘録として進めていきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#OpenCV SharpのVideoCapture.IsOpenedメソッドがtrueにならない

本内容を利用した場合の一切の責任を私は負いません。

コンストラクタで動画ファイルを指定した場合。
Readメソッドで取得したMatも使用できない。
動画ファイルのファイル名が原因ぽい。
「?」が入っていると例外は発生しないが事象が起こる。
Unicodeのせい???と検索したら、既に下記で問題視されてるように見える。
https://github.com/shimat/opencvsharp/issues/342
で、更に追うと、下記から元のOpenCVの問題っぽく見え、チケットがクローズしてないのでまだ修正されてないのかも。
https://github.com/opencv/opencv/pull/13368
とりあえず、ファイル名を変えれば回避できる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『Unity C# 初心者メモ』オブジェクトをランダムな座標へ一定時間ごとに移動させる

自分用メモ
わざと区切ってるけど全部1枚の流れ

必要になる素材を作る

public class RandomIdou : MonoBehaviour
{
    //座標を再計算しても良いかの判定
    private bool bl = true;

    //移動先の座標
    private Vector3 pos;

    //x座標代入用
    private float x;

    //z座標代入用
    private float z;

    //移動する速度
    private float speed = 3f;

    //移動範囲計算用
    private int i = 5;

初動用の座標を作る

    void Start()
    {
        //Randomな値を代入する
        x = Random.Range(-i, i);
        z = Random.Range(-i, i);

        //↑で出た値を座標に代入する
        pos = new Vector3(x, transform.position.y, z);
    }

座標の移動と再計算

    void Update()
    {
        //移動するときの速度用
        float step = speed * Time.deltaTime;

        //座標をpos座標へstepの速度で移動させる
        transform.position = Vector3.MoveTowards(transform.position, pos, step);

        //座標の再計算を行ってよく、座標が目的地に着いた時
        if (bl == true && transform.position == pos)
        {
            //2秒後再計算を行う
            Invoke("Idou", 2f);
            //毎フレーム計算しない様に判定を変える
            bl = false;
        }
    }

再計算用のメソッド

    void Idou()
    {
        //移動するときの値への減少率を計算
        float xp = 1f - System.Math.Abs(transform.position.x) / 5f;
        float zp = 1f - System.Math.Abs(transform.position.z) / 5f;

        //x座標が中心より大きい時(中央より右側へ行ったとき
        if (transform.position.x > 0)
        {
            //右側へ移動する範囲にのみ減少率を掛けRandomな値を計算する
            x = Random.Range(-i, i * xp);
        }
        //x座標が中心より小さい時(中央より左側へ行ったとき
        else
        {
            //左側へ移動する範囲にのみ減少率を掛けRandomな値を計算する
            x = Random.Range(-i * xp, i);
        }

        //y座標ver(以下略
        if (transform.position.z > 0)
        {
            z = Random.Range(-i, i * zp);
        }
        else
        {
            z = Random.Range(-i * zp, i);
        }

        //目的地座標posに計算結果の値を代入する
        pos = new Vector3(x, transform.position.y, z);

        //再計算をしてもよい判定を付ける
        bl = true;
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『Unity C# 個人用メモ』 WASDや矢印キーでオブジェクトの位置を動かす

自分用メモであり同じような初心者の方へ向けた自分なりの解説
名称がちゃんとあっているのかどうかは正直わかりませn

オブジェクトの位置を動かす場合

位置を一瞬で移動させる

object の  座標   は 座標宣言   x   y  z
transform.position = new Vector3( 3f, 0, 2f);

とすればオブジェクトの位置がx座標が3f,y座標は0,x座標が2fとなる

少しずつ移動させる

transform.position = new Vector3(transform.position.x, 
                  transform.position.y,
                  transform.position.z+0.2f);

void Update内にこのように書くと少しずつ奥に進むようになる
呼ばれる度にオブジェクトをz軸方向へ0.2fを足すという意味

WASDや矢印キーを入力をしたときに移動させる

適当に作ったゲームオブジェクトにRigidbodyコンポーネントを追加してから
以下の様にスクリプトを書く

public class NewBehaviourScript : MonoBehaviour
{
    //Rigidbody型のrbという変数を作る
    private Rigidbody rb;

    //float型のpowerという変数を作り、3fを代入する
    private float power = 3f;

    private void Start()
    {
        //リジットボディを取得しrbに代入する
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        //キー入力による値を代入(解説は後で
        float x = Input.GetAxisRaw("Horizontal");
        float z = Input.GetAxisRaw("Vertical");

        //↑で代入された値をVector3型のIdouHoukouという変数に代入する
        Vector3 IdouHoukou = new Vector3(x, 0, z);

        //rbに力を加える
        //velocityは質量などの物理演算を無視するときに使う
        rb.velocity = IdouHoukou * power;

        //rbの回転をさせない様にする
        rb.constraints = RigidbodyConstraints.FreezeRotation;

        //移動範囲の制限
        transform.position = new Vector3(
            Mathf.Clamp(transform.position.x, -5f, 5f),
            Mathf.Clamp(transform.position.y,1f,1f),
         Mathf.Clamp(transform.position.z, 5f, 5f));
    }
}

これでWASDや矢印キーを入力したときに移動が出来るようになります

Input.GetAxisRaw("Horizontal");
Input.GetAxisRaw("Vertical");

Input.GetAxisRaw
とは入力された物に対してint型(整数)で-1~1を返す
HorizontalA,D,がUnityに初期設定として登録されており、
Aが入力されている時は-1
何も入力されていない時は0
Dが入力されている時はを返す

同じようにVerticalは、
sが入力されている時は-1
何も入力されていない時は0
wが入力されている時はを返す

つまり先ほど書いた以下の

Vector3 idou = new Vector3(x, 0, z);

new Vector3(x, 0, z)このx部分とz部分に-1,0,1が代入される

その後

rb.velocity = idou * speed;

とあるので
xの値が-1ならx(-1)*speed(3f)となりx軸-3fの力を加える
xの値が0ならx(0)*speed(3f)となりx軸0の力を加える
xの値が1ならx(1)*speed(3f)となりx軸3fの力を加える
ということになる
y軸も同じ考えなので割愛

rb.constraints = RigidbodyConstraints.FreezeRotation;

こちらはゲームオブジェクトのインスペクタ内の
Rigidbodyコンポーネントの下部にある
constraintsという項目の2段目にある
回転するかどうかのチェックマークをスクリプト上でつけているだけなので
チェックマークを付けた場合は記入しなくても問題はありません。(てかチェック付けた方が楽)

このままだと入力がある限り際限なく移動し続けるので制限をつけます

transform.position = new Vector3(
            Mathf.Clamp(transform.position.x, -5f, 5f),
            Mathf.Clamp(transform.position.y,1f,1f),
         Mathf.Clamp(transform.position.z, -5f, 5f));

Mathf.Clamp(対象,最小値,最大値)を使用することで
簡単にtransform.positionMathf.Clamp()内の範囲以上には移動できなくなるという処理が作れます
使わない場合

        if (transform.position.x >= -5f)
        {
            transform.position = new Vector3(-5f, transform.position.y, transform.position.z);
        }

こんな感じの物をx,y,zそれぞれ2パターンで合計6回も書くことになるのでMathf.Clampは偉大です

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DataGridのSelectedItemsがbind出来ない!!

0. はじめに

Freeradicalの中の人、yamarahです。
普段は、Autodesk InventorのAddIn作成に関する記事を書いています。
今回は、WPFのDataGridにおいて、複数選択を許可した場合に、選択状態をどのようにViewModelにbindするかという記事です。
WPF自体は詳しくないので、おかしな点があればご指摘歓迎です。

20.02.21 17:55 JST 追記
やっぱり仮想化をオフしないと、連動しませんでした。
具体的には、視界外のIsSelectedtrueにしても、無視されるようです。
解決策が見つかれば、記事を更新します。work aroundがあれば、よろおねです。

1. 方針

まず前提として、DataGrid.SelectedItemsはbindできません。単選択なら問題になりませんが、複数選択の状態を知るには少し手間が必要です。
では、方針ですが、概ね次の記事に従います。

リンク : ListViewの選択項目を取得/設定する

つまりは、

ListViewItem.IsSelected に対するバインディングは OneWay (VM -> Vのみ)にする
ListView.SelectedItems の内容は一切信用しない
VからVMへの反映は、SelectionChanged イベントで自力でやる

ということです。
SelectedItemsのようなCollectionを用意するのではなく、各Item内のIsSelectedを選択状態と同期するようになります。
ただし、折角だからコードビハインドは嫌だよね、ってことで、Behaviorを使って実装します。

2. Item側の準備

Behaviorに汎用性を持たせたいので、IsCheckedはData型に直接書くのではなく、基底classに書いてそれを継承するようにします。

CollectionItemWithIsSelectedProperty.cs
using System.ComponentModel;

namespace TestApp
{
    public class CollectionItemWithIsSelectedProperty : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged = null;
        private bool _IsSelected;
        public bool IsSelected
        {
            get => _IsSelected;
            set
            {
                if (_IsSelected != value)
                {
                    _IsSelected = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
                }
            }
        }
    }
}

では、これを継承したテスト用のclassを作ります。

PathInfo.cs
using System.IO;

namespace TestApp
{
    class PathInfo : CollectionItemWithIsSelectedProperty
    {
        public string Directory { get; private set; }
        public string FileName { get; private set; }
        public PathInfo(string fullFileName)
        {
            Directory = Path.GetDirectoryName(fullFileName);
            FileName = Path.GetFileName(fullFileName);
        }
    }
}

3 View → ViewModelのBehavior

SelectionChangedをhookして、選択状態の変化を各Itemに設定します。

NotifyIsSelectedToSouceBehavior.cs
using Microsoft.Xaml.Behaviors;
using System.Windows.Controls;

namespace TestApp
{
    /// <summary>
    /// 選択状態をItem.IsSelectedに反映するBehavior
    /// </summary>
    class NotifyIsSelectedToSouceBehavior : Behavior<DataGrid>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.SelectionChanged += DataGrid_SelectionChanged;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.SelectionChanged -= DataGrid_SelectionChanged;
        }

        private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            foreach (CollectionItemWithIsSelectedProperty addedItem in e.AddedItems)
            {
                addedItem.IsSelected = true;
            }
            foreach (CollectionItemWithIsSelectedProperty removedItem in e.RemovedItems)
            {
                removedItem.IsSelected = false;
            }
        }
    }
}

4. SelectedItemsCountのBehavior

やっぱり、選択数は欲しいですよね。ItemsSourceをなめてIsSelectedを数えるのは非効率なので、これもBehaviorを作ります。

BindSelectedItemsCountBehavior.cs
using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;

namespace TestApp
{
    /// <summary>
    /// DataGrid内のSelectedItems.Countに相当する値を返すBehavior
    /// </summary>
    public class BindSelectedItemsCountBehavior : Behavior<DataGrid>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.SelectionChanged += DataGrid_SelectionChanged;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.SelectionChanged -= DataGrid_SelectionChanged;
            base.OnDetaching();
        }

        public static DependencyProperty SelectedItemsCountProperty =
            DependencyProperty.Register("SelectedItemsCount", typeof(int), typeof(BindSelectedItemsCountBehavior), new PropertyMetadata(null));

        public int SelectedItemsCount
        {
            get { return (int)GetValue(SelectedItemsCountProperty); }
            set { SetValue(SelectedItemsCountProperty, value); }
        }

        void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedItemsCount += e.AddedItems.Count;
            SelectedItemsCount -= e.RemovedItems.Count;
        }
    }
}

5. Interaction.Behaviorsを使えるようにする

さて、いよいよxamlに記載・・・の前に、xaml内にBehaviorを記述するのに準備が必要です。
ググると、BlendのSDKを・・・という記事がヒットしますが、VS2019では勝手が違うので注意。
リンク : 新しい Behavior ライブラリへの更新 #27
xamlの名前空間指定は、次のようになります。

<Window ・・・続く
        xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;
            assembly=Microsoft.Xaml.Behaviors" 

6. サンプルコード

まずは、Viewから。

TestView.xml
<Window x:Class="TestApp.TestView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors" 
        xmlns:local="clr-namespace:TestApp"
        mc:Ignorable="d"
        Title="TestView" Height="300" Width="300">
    <DockPanel>
        <TextBlock DockPanel.Dock="Top" Text="{Binding SelectedItemsCountReport}"/>
        <DataGrid ItemsSource="{Binding PathInfoList, Mode=OneWay}"   SelectionMode="Extended" IsReadOnly="True" 
                      GridLinesVisibility="None" HeadersVisibility="Column" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Width="SizeToHeader"  Header="IsSelected" Binding="{Binding IsSelected}"/>
                <DataGridTextColumn Width="*" Header="パス" Binding="{Binding Directory}"/>
                <DataGridTextColumn Width="SizeToCells" MinWidth="70" Header="ファイル名" Binding="{Binding FileName}"/>
            </DataGrid.Columns>
            <DataGrid.RowStyle>
                <Style TargetType="{x:Type DataGridRow}">
                    <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWay}"/>
                </Style>
            </DataGrid.RowStyle>
            <i:Interaction.Behaviors>
                <local:NotifyIsSelectedToSouceBehavior/>
                <local:BindSelectedItemsCountBehavior SelectedItemsCount="{Binding SelectedItemsCount, Mode=OneWayToSource}"/>
            </i:Interaction.Behaviors>
        </DataGrid>
    </DockPanel>
</Window>
  • ViewModel → ViewのIsSelectedのbindは、
            <DataGrid.RowStyle>
                <Style TargetType="{x:Type DataGridRow}">
                    <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWay}"/>
                </Style>
            </DataGrid.RowStyle>
  • View → ViewModelのIsSelectedSelectedItemsCountのbindは、
            <i:Interaction.Behaviors>
                <local:NotifyIsSelectedToSouceBehavior/>
                <local:BindSelectedItemsCountBehavior SelectedItemsCount="{Binding SelectedItemsCount, Mode=OneWayToSource}"/>
            </i:Interaction.Behaviors>

です。bind方向の指定は、上記の通りに忘れず設定してください。せっかくコードを書いたんだから、SelectedItemsCountのModeのDefault値をOneWayToSourceにしたかったのですが、簡単には出来ないようなので諦めました。

次に、ViewModelです。

TestViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace TestApp
{
    class TestViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        public ObservableCollection<PathInfo> PathInfoList { get; private set; }

        private int _SelectedItemsCount;
        public int SelectedItemsCount
        {
            get => _SelectedItemsCount;
            set
            {
                if (_SelectedItemsCount != value)
                {
                    _SelectedItemsCount = value;
                    SelectedItemsCountReport = $"SelectedItemsCount : {_SelectedItemsCount}";
                }
            }
        }

        private string _SelectedItemsCountReport = string.Empty;
        public string SelectedItemsCountReport
        {
            get => _SelectedItemsCountReport;
            set
            {
                if (_SelectedItemsCountReport != value)
                {
                    _SelectedItemsCountReport = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemsCountReport)));
                }
            }
        }

        public TestViewModel()
        {
            PathInfoList = new ObservableCollection<PathInfo>
            {
                new PathInfo(@"C:\temp\file0001.txt") { IsSelected = true },
                new PathInfo(@"C:\temp\file0002.txt") { IsSelected = true },
                new PathInfo(@"C:\temp\file0003.txt") { IsSelected = false },
                new PathInfo(@"C:\temp\file0004.txt") { IsSelected = true },
                new PathInfo(@"C:\temp\file0005.txt") { IsSelected = false }
            };
        }
    }
}

実際のアプリケーションでは、SelectedItemsCountにReactivePropertyなどを使うことになるでしょう。

7. 動作結果

では、これらのコードでWindowを表示してみましょう。(スタートアップのコードは、各自でどうぞ)
TestWindow.png
選択状態を変化させると、上端の選択数と、左端桁のTrue/Falseが変化するのが見て取れます。

8. とはいえ、SelectedItemsが欲しいよね

という方は、次の記事を参考にどうぞ。
リンク : WPFのDataGridで選択された複数のアイテムをバインドするためのビヘイビア

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『Unity C#』ガチ初心者の自分用メモ 型とか演算子

よく使う型

int ・・・整数
/// -1,0,1 
flot・・・小数点以下を含む
/// fが必須 -1.5f,1.5f 
string・・・文字列
/// ""が必須 "こんにちは"
bool()・・・真偽判定
/// true(真) or false(偽)
Vector3(2)・・・座標とかベクトル
/// VectorのVは大文字なのを忘れないで!!
/// 使う時は Vector3 zahyou = new Vector3(〇,〇,〇)の様に宣言する
/// Vector2の場合なら new Vector2(〇,〇)の様な感じ
GameObject・・・ゲームオブジェクト
/// 用途によってGameObjectとgameObjectの二つ使い分けるので気を付ける
/// 宣言するときは```private GameObject ○○ ```など
/// 使用するときは```gameObject.GetComponent<〇〇>();```など
Transform・・・トランスフォーム。オブジェクトのインスペクタ(Unity画面の右側)の上の方にある座標とか回転入力するときに使うやつ
/// 宣言するときは```private Transform ○○;```など
///使用するときは```transform.position;```など

演算子

計算で使うやつ
四則演算省略
+= 追加(=が後ろ)
-= 差引(=が後ろ)
++ 1増やす
-- 1減らす
=  代入

if分で使うやつ
== 同じであるとき
!= 同じではない時
&& どちら[も]
|| どちら[か]
>  大なり
>= 大なりイコール(=が後ろ)
<  小なり
>= 小なりイコール(=が後ろ)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『Unity C# 個人用メモ』 ガチ初心者の自分用の用語メモ

初心者の自分用のメモ
大体こんな感じだよって思ってればいいやつ

MonoBehaviour(モノビヘイビア)

全てのスクリプトから派生するベースクラス

ヒエラルキービュー

画面左。シーンビューに配置したオブジェクトの一覧、階層構造。

プロジェクトビュー

画面下。ゲームで使う素材を管理。

インスペクタ

画面右。選択しているオブジェクトの詳しい情報がわかる。 座標・オブジェクトの大きさ・配置など。

メソッド

void Start()
{

}

メソッドとはこのvoid 〇〇のこと。上の物であればStart()がメソッドになる

アクセス修飾子

public int = 1;
private int = 1;

アクセス修飾子とはこのpublicやprivateのこと

変数・型

int a ;

int ← 型
a  ← 変数
intという型のaという変数を作るという意味になる

引数(ひきすう)

Vector3 zahyou = new Vector3(〇,〇,〇);

new Vector3(〇,〇,〇);〇のことなど

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非同期ストリーム IAsyncEnumerable<T> から一定個数ずつ列挙する

このドキュメントの内容

C# 8.0 で追加された非同期ストリーム(IAsyncEnumerable<T>)から一定個数ずつオブジェクトを列挙する方法を紹介します。一定個数ずつバッファリングしながら処理を行いたい、バッファリングの手段はそれぞれの処理の実装に委ねたいというような場面で使うことを想定しています。

IAsyncEnumerable<T> インターフェースに対する拡張メソッドとして実装しました。

Chunkメソッド
/// <summary>
/// 指定された非同期ストリームから一定個数ずつオブジェクトを列挙します。
/// </summary>
/// <param name="enumerable">列挙対象の非同期ストリーム</param>
/// <param name="chunkSize">一度に取得する個数</param>
/// <param name="cancellationToken">キャンセルトークン</param>
/// <returns>一定個数ずつオブジェクトを列挙する非同期ストリーム</returns>
public static IAsyncEnumerable<IAsyncEnumerable<T>> Chunk<T>(
    this IAsyncEnumerable<T> enumerable
    , int chunkSize
    , CancellationToken cancellationToken = default
)

サンプルコード

あまり現実的な内容ではありませんが、ArrayPool<T> と Span<T> を使って一定個数ずつバッファリングする例です。

private static async Task ChunkEnumerateSample()
{
    SampleData[] buffer = ArrayPool<SampleData>.Shared.Rent(3);
    try
    {
        // 3つずつ列挙する
        await foreach (var chunk in GetSampleData().Chunk(3))
        {
            Console.WriteLine("----- start chunk -----");
            await DoSomethingAsync(chunk, buffer);
            Console.WriteLine("----- end chunk -----");
        }
    }
    finally
    {
        ArrayPool<SampleData>.Shared.Return(buffer);
    }
}

private static async Task DoSomethingAsync(IAsyncEnumerable<SampleData> enumerable, SampleData[] buffer)
{
    int count = 0;
    await foreach (var obj in enumerable)
    {
        buffer[count++] = obj;
    }
    DoSomething(buffer, count);
}

private static void DoSomething(SampleData[] buffer, int length)
{
    var span = new Span<SampleData>(buffer, 0, length);

    for (int i = 0; i < span.Length; ++i)
    {
        Console.WriteLine($"{span[i].Value}");
    }
}

// 10個のオブジェクトを返す
private static async IAsyncEnumerable<SampleData> GetSampleData()
{
    await Task.Yield();

    for (int i = 0; i < 10; ++i)
    {
        yield return new SampleData(i);
    }
}

public class SampleData
{
    public SampleData(int value)
    {
        Value = value;
    }
    public int Value { get; }
}

拡張メソッド Chunk の実装内容

public static class AsyncEnumerableExtensions
{
    /// <summary>
    /// 指定された非同期ストリームから一定個数ずつオブジェクトを列挙します。
    /// </summary>
    /// <param name="enumerable">列挙対象の非同期ストリーム</param>
    /// <param name="chunkSize">一度に取得する個数</param>
    /// <param name="cancellationToken">キャンセルトークン</param>
    /// <returns>一定個数ずつオブジェクトを列挙する非同期ストリーム</returns>
    public static IAsyncEnumerable<IAsyncEnumerable<T>> Chunk<T>(
        this IAsyncEnumerable<T> enumerable
        , int chunkSize
        , CancellationToken cancellationToken = default
    )
    {
        return Chunk(enumerable, chunkSize, new NullState(), cancellationToken);
    }

    /// <summary>
    /// 指定された非同期ストリームから一定個数ずつオブジェクトを列挙します。
    /// </summary>
    /// <param name="enumerable">列挙対象の非同期ストリーム</param>
    /// <param name="chunkSize">一度に取得する個数</param>
    /// <param name="state">列挙状態を管理するオブジェクト</param>
    /// <param name="cancellationToken">キャンセルトークン</param>
    /// <returns>一定個数ずつオブジェクトを列挙する非同期ストリーム</returns>
    public static async IAsyncEnumerable<IAsyncEnumerable<T>> Chunk<T>(
        this IAsyncEnumerable<T> enumerable
        , int chunkSize
        , IAsyncEnumerablorState state
        , [EnumeratorCancellation]CancellationToken cancellationToken = default
    )
    {
        await using (IAsyncEnumerator<T> enumerator = enumerable.GetAsyncEnumerator(cancellationToken));

        while (!state.IsEof)
        {
            yield return new ChunkAsyncEnumerable<T>(enumerator, chunkSize, state);
        }
    }

    private class NullState : IAsyncEnumerablorState
    {
        internal bool IsEof { get; set; }
        internal void OnMoveNext() {}
    }

    private readonly struct ChunkAsyncEnumerable<T> : IAsyncEnumerable<T>
    {
        internal ChunkAsyncEnumerable(
            IAsyncEnumerator<T> enumerator
            , int chunkSize
            , IAsyncEnumerablorState state
            )
        {
            m_Enumerator = enumerator;
            m_ChunkSize = chunkSize;
            m_State = state;
        }

        private readonly IAsyncEnumerator<T> m_Enumerator;
        private readonly int m_ChunkSize;
        private readonly IAsyncEnumerablorState m_State;

        private async IAsyncEnumerable<T> Enumerate()
        {
            int count = 0;

            while (await m_Enumerator.MoveNextAsync().ConfigureAwait(false))
            {
                yield return m_Enumerator.Current;
                ++count;
                m_State.OnMoveNext();
                if (count >= m_ChunkSize) { yield break; }
            }

            m_State.IsEof = true;
        }

        public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
        {
            return Enumerate().GetAsyncEnumerator(cancellationToken);
        }
    }

}

public interface IAsyncEnumerablorState
{
    bool IsEof { get; set; }
    void OnMoveNext();
}

サンプルコードの実行結果

3個ずつ列挙され、最後の端数も正しく列挙されています。

----- start chunk -----
0
1
2
----- end chunk -----
----- start chunk -----
3
4
5
----- end chunk -----
----- start chunk -----
6
7
8
----- end chunk -----
----- start chunk -----
9
----- end chunk -----
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rx.NETでWindows Formを操作するメモ

ポーリング処理のサンプル

  1. System.ReactiveをNUGETから入れる
  2. this.ticker= Observable.Interval(new TimeSpan(0, 0, 15));でObserverを作る
this.ticker.Subscribe(
    (item) => {
        // 任意のコントロールを操作する例
        // Control.Invokeでコントロールを操作するメソッドを呼び出すと実現できる
        this.AnyControl.Invoke(new UseControlDelegate(this.UseControlMethod));
    }
);

どうでもいいけどRx.NETってちゃんとしたリファレンスなくね?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む