Ognacの雑感

木漏れ日々

目次

Blog 利用状況

書庫

ギャラリ

強引なジェネリック?

ジェネリックは型を抽象化することでメリットがでます。
ジェネリック・ルーチン内で型判断して、処理を分岐するのに違和感感じるのは私が変なのか?
Castができないから ((str_str)(object)ss) とするのは、強引すぎる感じがするのは私だけ?
 確かに   ((str_str)ss)  とするとCompileErrorになるが、((str_str)(object)ss)とするとコンパイルできる。......うーん。いいのかもしれないが。


        public List<T> get_Analize<T>() where T : new()
        {
            List<T> rc = new List<T>();

            string pat = null;
            if (typeof(T) == typeof(str_str)) pat = @"(.*)\((.*)\)";
            if (typeof(T) == typeof(decimal_str)) pat = @"([-|+|\d|\.]*)(?<=\d)(.*)";

            Regex r = new Regex(pat, Option.ig);
            Match m = r.Match(_src);
            while (m.Success)
            {
                T ss = new T();
                if (typeof(T) == typeof(str_str))
                {
                    ((str_str)(object)ss).str1 = m.Groups[1].Value;
                    ((str_str)(object)ss).str2 = m.Groups[2].Value;
                    rc.Add(ss);
                }
                if (typeof(T) == typeof(decimal_str))
                {
                    decimal d = 0;
                    if (decimal.TryParse(m.Groups[1].Value, out d))
                    {
                        ((decimal_str)(object)ss).dec = d;
                        ((decimal_str)(object)ss).str = m.Groups[2].Value;
                        rc.Add(ss);
                    }
                }
                m = m.NextMatch();
            }
            return rc;
        }

投稿日時 : 2008年5月15日 1:17

Feedback

# re: 強引なジェネリック? 2008/05/15 14:05 凪瀬

私ならtypeof(T)部分の分岐をStrategyパターンにしてT型に押し込めますね。

# re: 強引なジェネリック? 2008/05/15 21:55 Pasie.

IAnalyzeインターフェイスにget_Analizeメソッドを用意して、str_strとdecimal_strにIAnalyzeインターフェイスを実装して、それぞれのget_Analizeメソッドで検索して、結果をList<T>を戻したらだめなんでしょうか?

上記のコードは型がフラグになっていることと、str_strとdecimal_str以外の型が渡されたときどうなんるんだろうとか思ってしまい、かなり違和感があります。

# re: 強引なジェネリック? 2008/05/16 0:39 Ognac

コメントありがとうございます。
>StrategyパターンにしてT型に押し込めますね。 Remove Comment 137818
>IAnalyzeインターフェイスにget_Analizeメソッドを用意して、
そうですよね。引数というか、対処したい実体の型クラスに特有な処理なので、型クラスに実装するのが自然。Interfaceで括るというのが、 Strategy に通じるものがあり、すっきりします。

問題なのか、彼のソースを書いたチームが、本質を理解しないで、うわべのジェネリックを使ったことにあるかと。でもこれでレビューがOKになっているので、なんともできない....orz.

別次元ですが, ((str_str)ss) のキャストがダメで、、((str_str)(object)ss)がOKというのは、
具現型から具現型のキャストがダメという仕様からでしょうが、<T>の構文のなかで具現型へのキャストを許すというのは 、少し引っかかる。 昔,CかMFCの世界で、(xx)(yy)zz;の二重キャストが必要な局面がありましたが、...どの理由だったか忘れました..ww(^^。

# re: 強引なジェネリック? 2008/05/16 22:25 Pasie.

>具現型から具現型のキャストがダメ

意味がよく理解できないのですが、((str_str)ss) がだめなのは、単純にstr_strとdecimal_strにインターフェイスの互換がないからではないでしょうか。インターフェイスに互換がない以上castができないのは当然だと思います。

たとえば、基底クラスclass1を継承した、class2,class3を作ったとき、
 (class2) New Class3();
はだめですが、
 (class2)((class1) New Class3());
はOKです。一旦インターフェイスが互換するところまでcastしたのちに、downcastしろってことじゃないかと思います。

しかしdowncastは、それだけで危険なコードだと思うわけですが、それにしても上記コードはなんのために共通化しているのか、何度読んでもわからない。単純に2つのクラスにしちゃってもいいのではないでしょうか?目的が分からないのでなんとも言えませんが。

# re: 強引なジェネリック? 2008/05/16 22:56 Ognac

public class Use_Regex
{
//--------------------
private String _src;
private string _pattern;
private RegexOptions _ro;
//--------------------
public Use_Regex(I_RegexAnalize ira, String s)
{
_src = s;
_pattern = ira.regexPattern;
_ro = ira.regexOption;
Regex r = new Regex(_pattern, _ro);
Match m = r.Match(_src);
while (m.Success)
{
ira.listAdd(m.Groups);
m = m.NextMatch();
}

}
}
//■■
//---------------------------------
public interface I_RegexAnalize
{
string regexPattern{ get;}
RegexOptions regexOption { get; }
void listAdd(GroupCollection gm);
}
//---------------------------------
public class Id_Value_mgr:I_RegexAnalize
{
//-----------------------------------
private List<Id_Value> _list;
public List<Id_Value> getList()
{
return _list;
}
//-----------------------------------
public Id_Value_mgr()
{
_list = new List<Id_Value>();
}
//--------------------------------
public RegexOptions regexOption
{
get {return RegexOptions.IgnoreCase | RegexOptions.Compiled; }
}
//--------------------------------
private string _regexPattern = @"(.*)\((.*)\)";
public string regexPattern
{
get { return _regexPattern; }
}
//--------------------------------
public void listAdd(GroupCollection gm)
{
_list.Add(new Id_Value(gm[1].Value, gm[2].Value));
}
//-----------------------------------
public override string ToString()
{
StringBuilder sb = new StringBuilder(1024);
foreach(Id_Value lv in _list)
{

sb.AppendLine(lv.ToString());
}
return sb.ToString();
}
//-----------------------------------
//-----------------------------------
}
public class Id_Value
{
//--------------------------------
private string _id;
public string Id
{
get { return _id; }
set { _id = value; }
}
//--------------------------------
private string _value;
public string Value
{
get { return _value; }
set { _value = value; }
}
//--------------------------------
public Id_Value(string id_, string value_)
{
Id = id_;
Value = value_;
}
//-----------------------------------
public override string ToString()
{
return string.Format("{0} : {1}", Id, Value);
}

}

ソースが長くなるのは、不可避かな?、妙案があるかも、今後かんがえよっと。


# re: 強引なジェネリック? 2008/05/17 1:14 Pasie.

徒然なるままに書いたらインターフェイスもくそもなくなってしまった…

Public Class str_str
Public str1 As String
Public str2 As String
End Class

Public Class decimal_str
Public dec As Decimal
Public str As String
End Class

Public Class str_str_list
Public list As New Generic.List(Of str_str)

Public Sub Analyze(ByVal inSrc As String)
Dim re As New Regex("(.*)\((.*)\)", RegexOptions.IgnoreCase)
Dim m As Match = re.Match(inSrc)
Do While (m.Success)
Dim s As New str_str
s.str1 = m.Groups(1).Value
s.str2 = m.Groups(2).Value
list.Add(s)
m = m.NextMatch()
Loop
End Sub
End Class

Public Class decimal_str_list
Public list As New Generic.List(Of decimal_str)

Public Sub Analyze(ByVal inSrc As String)
Dim re As New Regex("([-|+|\d|\.]*)(?<=\d)(.*)", RegexOptions.IgnoreCase)
Dim m As Match = re.Match(inSrc)
Do While (m.Success)
Dim d As Decimal
If (Decimal.TryParse(m.Groups(1).Value, d)) Then
Dim s As New decimal_str
s.dec = d
s.str = m.Groups(2).Value
list.Add(s)
m = m.NextMatch()
End If
Loop
End Sub
End Class

Public Shared Sub Main()
Dim list_s As New str_str_list
list_s.Analyze("")

Dim list_d As New decimal_str_list
list_d.Analyze("")
End Sub

# re: 強引なジェネリック? 2008/05/17 2:50 Ognac

いいですねぇ。無理にジェネリックを使わないほうがシンプルとも言えますね。

>単純にstr_strとdecimal_strにインターフェイスの互換がないからではないでしょうか
そうですね。気付かなかったです。考えてみれば至極当然なことですね。ハマるところでした。

上記のソースの真意は未だ不明...というより、強引にジェネリックとOOPを両立させようとした結果だと想像してます。
 例でまずいのは、 型で処理を分けるという発想と、正規表現の処理を一か所にまとめようとした点だと思います。
 想像の域をでませんが、処理を切り分けるのに、正規表現という切り口で纏めたように思うのです。 str_str や decimal_str のクラスに依存すべき、正規表現パターンや、List<T> 型の問題などのobjectの継承関係やメソッドを(実際に発生する物のクラスが考えるべきなのに)、正規表現処理という「処理」のくくりでOOP化しようとしたからだと思います。
 間違った学習した人に「間違ってますよ。」と指摘すべきレビュアーの認識レベルがそこ至っていない悲しい現実。
 この例の元になった仕様でも、実装パターンはいくつもでそうですね。部品化やOOP化は継承/Interfaceの単位の切り分けで、大きくかわりますね。今回の例のように、正規表現処理でまとめようとすると間違ってしまいます。良き反面教師だと思えてきました。

# re: 強引なジェネリック? 2008/05/17 23:02 Pasie.

>正規表現の処理を一か所にまとめようとした点

そうですね。その気持ちは分かります。

特に上記の私のコードの、str_str_list とdecimal_str_list はほとんど同じコードなので、これを共通化するわけにはいかないの?という指摘は出そうです。

個人的には、これは(str_strとdecimal_strのユニーク部分を除けば)正規表現検索の最小のイテレーションループなので、これを共通化して外に出すというのは、For~Nextを外に出すというのと一緒ですよ。と反論したいところなんですが、実際のところどうなんでしょうね。

私も、イテレーションループ内でstr_strとdecimal_strで共通に行う前処理とかあるとか、対象クラスが2つではなく100個あるとかなら、場合によってはイテレータを作って、ユニーク部分の処理はinterfaceを切って渡すとかするかもしれませんが。

この程度だったら、かえってイテレータ作った方が可読性とメンテ性が落ちそうな気がします。

以上、いいわけでした ^_^;;

# re: 強引なジェネリック? 2008/05/18 1:19 Ognac

>この程度だったら、かえってイテレータ作った方が可読性とメンテ性が落ちそうな気がします。
結局、正解というのは無いんでしょうね。
適用パターンが少ない時は、エレガントにすると逆に足枷になることがある.....と指摘する人もいます。
 しかし、パターン数が一定量(私は3つ以上にしてますが)を超えると、効果ありと考えています。

# rgOjVbYefbzVY 2014/07/19 17:27 http://crorkz.com/

tmfqZA Enjoyed every bit of your article post. Really Great.

タイトル
名前
Url
コメント