Lambda 및 대표와의 리팩토링
-
03-07-2019 - |
문제
방금 vs2008을 설치했으며 Lambda 또는 대표 (또는 조합)로 해결할 수있는 문제를 해결했습니다.
private string ReadData(TcpClient s, string terminator)
{
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminator));
return sb.ToString();
}
문제는 때로는 문자열에 두 가지 다른 값이 포함되어 있는지 확인해야한다는 것입니다. 때로는 세 가지 값을 확인해야 할 수도 있습니다.
따라서 내가 제안하는 것은 "!
다음과 같은 다른 기능을 쓸 수 있습니다.
private bool compare1(string s, string t) {
return s.contains(t)
}
private bool compare2(string s, string t1, string t2) {
return (s.compare(t1) or s.compare(t2)
}
// etc...
그런 다음 3 가지 다른 값과 비교할 때 이러한 기능 중 하나에 대의원을 작성한 다음이를 readData () 메소드로 전달하십시오.
나는 대표자들에게는 매우 단서가 없으며, 이것이 람다에 적합한 장소처럼 보이는지 확실하지 않지만 무언가가 나에게 말하고 있습니다.
호출 코드는 다음과 같습니다.
// Enter username .
if (HasData(s,"login:"))
SendData(s, switchUser + TelnetHelper.CRLF);
Hasdata는 readData와 동일하지만 문자열 대신 BOOL을 반환합니다 (일부 속임수를 사용하여 하나의 방법으로 고려하고 싶습니다. 그러나 이것은 두 번째 질문입니다. 자유롭게 대답하십시오.
참조를 위해 :
private bool HasData(TcpClient s, string terminator)
{
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminator));
return sb.ToString().Contains(terminator);
}
해결책
술어 기능을 찾고있는 것 같습니다. 수표를 하드 코딩하는 대신 수표를 수행 할 수있는 것보다 대의원을 매개 변수로 사용하십시오.
private string ReadData(TcpClient s, Func<string,bool> predicate)
{
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !predicate(sb));
return sb.ToString();
}
그런 다음 적절한 대의원을 생성하고 전달하는 몇 개의 포장지를 만들 수 있습니다.
public bool HasData(TcpClient c, string terminator) {
return HasData(c, (s) => s.Contains(terminator));
}
public bool HasData(TcpClient c, string t1, string t2) {
return HasData(c, (s) => s.Contains(t1) || s.Contains(t2));
}
임의의 터미네이터 수를 기반으로 한 대의원을 즉시 구축 할 수도 있습니다.
public bool HasData(TcpClient c, params string[] terminatorList) {
return HasData(c, (s) => terminatorList.Where(x => s.Contains(x)).Any());
}
다른 팁
한 가지 옵션은 readData () 메소드를 과부하하는 것입니다. 확인하는 값이 포함 된 문자열 배열을 가져 오는 것입니다. 사용 확장 방법, 당신은 contain ()을 확장하여 문자열 배열을 가져갈 수 있습니다.
readData () 메소드는 다음과 같습니다.
private string ReadData(TcpClient s, string[] terminators) {
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminators));
return sb.ToString();
}
continates () 메소드 확장자는 다음과 같습니다.
public static bool Contains ( this String str , String[] testValues )
{
foreach ( var value in testValues )
{
if ( str.Contains( value ) )
return true;
}
return false;
}
이 구현은 테스트 할 다양한 문자열이있을 때마다 새로운 술어를 만들 필요가 없습니다.
Lambdas의 구문은 나 자신 (그리고 내 팀의 나머지 부분)에 대해 다소 외국이기 때문에 나는 약간 다른 해결책을 가졌다. 위의 .Any () 함수에서 수정 될 때 .all ()의 구문을 알 수 없었습니다.
목록의 모든 터미네이터가 발견되도록 .all () 기능도 필요했습니다. 그래서 나는 다음과 같은 것과 같은 것으로 끝났습니다.
delegate bool Predicate (string s, params [] string terminators);
bool HasAll(string s, params string [] terminators) {
foreach (var t in terminators) {
if (!s.contains(t)) return false;
}
return true;
}
bool HasAny(string s, params string [] terminators) {
foreach (var t in terminators) {
if (s.contains(t)) return true;
}
return false;
}
// Just looking now, I could also pass in a bool to switch between the two and remove one of these functions. But this is fairly clear
string ReadData(TcpClient sock, Function predicate, params [] string terminators) {
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !predicate(sb.ToString(), terminators);
return sb.ToString();
}
그런 다음 호출 코드는 다음과 같습니다.
private void someFunc()
{
Predicate any = new Predicate(HasAny);
Predicate all = new Predicate(HasAll);
String response;
// Check all strings exist
response = ReadData(this.sock, all, "(", ")", "->")
if (all(response, "(", ")", "->")
SendData(this.sock, ...);
// Check any string exists
response = ReadData(this.sock, any, "Hi", "Hey", "Hello");
if (any(response, "Hi", "Hey", "Hello"))
SendData(this.sock, ...);
}
아마도 null 검사를 [모든 | all] 함수에 추가하고, do를 반대로 반전시키고, 파라를 복제하는 대신 응답을 확인하십시오! = null을 확인하십시오. 이 솔루션은 내 모든 사용 사례에 적합하며 상당히 인간 읽을 수 있습니다. 내가 위에서 언급 한 작은 변경을하는 한.
이 모든 것이 저에게 강조 표시됩니다. 그래도 Lambda 표현을 배워야 할 필요성!